type parameter library hinting python templates generic-programming

parameter - typing io python



Genéricos/plantillas en python? (7)

¿Cómo maneja python los escenarios genéricos / tipo de plantilla? Digamos que quiero crear un archivo externo "BinaryTree.py" y hacer que maneje árboles binarios, pero para cualquier tipo de datos.

Así que podría pasarle el tipo de un objeto personalizado y tener un árbol binario de ese objeto. ¿Cómo se hace esto en Python?


Afortunadamente ha habido algunos esfuerzos para la programación genérica en python. Hay una biblioteca: generic

Aquí está la documentación para ello: http://generic.readthedocs.org/en/latest/

No ha progresado durante años, pero puede tener una idea aproximada de cómo usar y crear su propia biblioteca.

Aclamaciones


Dado que python está tipeado dinámicamente, esto es muy fácil. De hecho, tendrías que hacer un trabajo extra para que tu clase BinaryTree no funcione con ningún tipo de datos.

Por ejemplo, si desea que los valores clave que se usan para colocar el objeto en el árbol disponible dentro del objeto desde un método como key() , simplemente llame a key() en los objetos. Por ejemplo:

class BinaryTree(object): def insert(self, object_to_insert): key = object_to_insert.key()

Tenga en cuenta que nunca necesita definir qué tipo de clase es object_to_insert. Siempre que tenga un método key() , funcionará.

La excepción es si desea que funcione con tipos de datos básicos, como cadenas o enteros. Tendrás que envolverlos en una clase para que funcionen con tu BinaryTree genérico. Si eso suena demasiado pesado y quieres la eficiencia extra de solo almacenar cadenas, lo siento, Python no es bueno en eso.


Debido a que Python se tipea dinámicamente, los tipos de objetos no importan en muchos casos. Es una mejor idea aceptar cualquier cosa.

Para demostrar lo que quiero decir, esta clase de árbol aceptará cualquier cosa para sus dos ramas:

class BinaryTree: def __init__(self, left, right): self.left, self.right = left, right

Y podría ser usado así:

branch1 = BinaryTree(1,2) myitem = MyClass() branch2 = BinaryTree(myitem, None) tree = BinaryTree(branch1, branch2)


Después de pensar en hacer tipos genéricos en python, comencé a buscar otros que tuvieran la misma idea, pero no pude encontrar ninguno. Asi que aqui esta. Probé esto y funciona bien. Nos permite parametrizar nuestros tipos en python.

class List( type ): def __new__( type_ref, member_type ): class List( list ): def append( self, member ): if not isinstance( member, member_type ): raise TypeError( ''Attempted to append a "{0}" to a "{1}" which only takes a "{2}"''.format( type( member ).__name__, type( self ).__name__, member_type.__name__ ) ) list.append( self, member ) return List

Ahora puede derivar tipos de este tipo genérico.

class TestMember: pass class TestList( List( TestMember ) ): def __init__( self ): super().__init__() test_list = TestList() test_list.append( TestMember() ) test_list.append( ''test'' ) # This line will raise an exception

Esta solución es simplista y tiene sus limitaciones. Cada vez que creas un tipo genérico, creará un nuevo tipo. Por lo tanto, las clases múltiples que heredan List( str ) como padre heredarían de dos clases separadas. Para superar esto, debe crear un dict para almacenar las diversas formas de la clase interna y devolver la clase interna creada anteriormente, en lugar de crear una nueva. Esto evitaría que se creen tipos duplicados con los mismos parámetros. Si está interesado, se puede hacer una solución más elegante con decoradores y / o metaclases.


En realidad ahora puedes usar genéricos en Python 3.5+. Vea PEP-484 y escriba la documentación de la biblioteca .

De acuerdo con mi práctica, no es muy claro y sin interrupciones, especialmente para aquellos que están familiarizados con los genéricos de Java, pero que todavía se pueden usar.


Mira cómo lo hacen los contenedores integrados. dict y list y así sucesivamente contienen elementos heterogéneos de cualquier tipo que te guste. Si define, digamos, una función de insert(val) para su árbol, en algún momento hará algo como node.value = val y Python se encargará del resto.


Python usa tipado de pato , por lo que no necesita una sintaxis especial para manejar varios tipos.

Si eres de un fondo C ++, recordarás que, siempre que las operaciones utilizadas en la función / clase de la plantilla estén definidas en algún tipo T (a nivel de sintaxis), puedes usar ese tipo T en la plantilla.

Entonces, básicamente, funciona de la misma manera:

  1. defina un contrato para el tipo de elementos que desea insertar en el árbol binario.
  2. documentar este contrato (es decir, en la documentación de la clase)
  3. implementar el árbol binario usando solo las operaciones especificadas en el contrato
  4. disfrutar

Notará, sin embargo, que a menos que escriba una verificación de tipo explícita (que generalmente no se recomienda), no podrá exigir que un árbol binario contenga solo elementos del tipo elegido.