Genéricos / plantillas en python?

¿Cómo maneja Python los escenarios de tipo genérico / 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 objeto personalizado y tener un árbol binario de ese objeto. ¿Cómo se hace esto en python?

Python utiliza la escritura de pato , por lo que no necesita una syntax especial para manejar múltiples tipos.

Si tiene antecedentes en C ++, recordará que, siempre que las operaciones utilizadas en la función / clase de la plantilla estén definidas en algún tipo T (en el nivel de syntax), puede 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 utilizando solo las operaciones especificadas en el contrato.
  4. disfrutar

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

De hecho, ahora puedes usar generics en Python 3.5+. Ver PEP-484 y escribiendo documentación de la biblioteca .

De acuerdo con mi práctica, no es muy transparente y claro, especialmente para aquellos que están familiarizados con Java Genéricos, pero aún son utilizables.

Después de tener algunos buenos pensamientos sobre la creación de tipos generics en python, comencé a buscar a otros que tenían 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 cree un tipo genérico, creará un nuevo tipo. Por lo tanto, varias clases que heredan List( str ) como padre heredarían de dos clases separadas. Para superar esto, debe crear un dictado 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 la creación de tipos duplicados con los mismos parámetros. Si está interesado, se puede hacer una solución más elegante con decoradores y / o metaclases.

Dado que Python se escribe dinámicamente, esto es muy fácil. De hecho, tendría que hacer un trabajo adicional para que su clase BinaryTree no funcione con ningún tipo de datos.

Por ejemplo, si desea que los valores de clave que se utilizan 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. Mientras 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á que envolverlos en una clase para que trabajen con su BinaryTree genérico. Si eso suena demasiado pesado y quieres la eficiencia adicional de almacenar cadenas, perdón, no es eso lo que hace Python.

Debido a que Python se escribe 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 por sus dos twigs:

 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) 

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

Afortunadamente, se han realizado algunos esfuerzos para la progtwigción genérica en python. Hay una biblioteca: genérica

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

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

Aclamaciones

Si usas Python 2 o quieres reescribir el código java. Su no es una solución real para esto. Esto es lo que consigo trabajando en una noche: https://github.com/FlorianSteenbuck/python-generics Todavía no obtengo ningún comstackdor, así que actualmente lo usas así:

 class A(GenericObject): def __init__(self, *args, **kwargs): GenericObject.__init__(self, [ ['b',extends,int], ['a',extends,str], [0,extends,bool], ['T',extends,float] ], *args, **kwargs) def _init(self, c, a, b): print "success c="+str(c)+" a="+str(a)+" b="+str(b) 

TODOs

  • Comstackdor
  • Haga que las clases y los tipos generics funcionen (para cosas como > )
  • Añadir soporte super
  • Añadir ? apoyo
  • Código de limpieza