2011-10-06 11 views
10

Quiero crear dinámicamente clases en tiempo de ejecución en python.¿Cuál es la ventaja de usar `exec` sobre` type() `al crear clases en tiempo de ejecución?

Por ejemplo, yo desea replicar el código de abajo:

>>> class RefObj(object): 
...  def __init__(self, ParentClassName): 
...   print "Created RefObj with ties to %s" % ParentClassName 
... class Foo1(object): 
...  ref_obj = RefObj("Foo1") 
... class Foo2(object): 
...  ref_obj = RefObj("Foo2") 
... 
Created RefObj with ties to Foo1 
Created RefObj with ties to Foo2 
>>> 

... pero quiero las clases foo1, Foo2, Foo a ser creados dinámicamente (es decir: durante la ejecución en lugar de en la primera pasada compilar).

Una forma de lograr esto es con type(), así:

>>> class RefObj(object): 
...  def __init__(self, ParentClassName): 
...   print "Created RefObj with ties to %s" % ParentClassName 
... def make_foo_class(index): 
...  name = "Foo%s" % index 
...  return type(name, (object,), dict(ref_obj = RefObj(name))) 
... 
>>> Foo1 = make_foo_class(1) 
Created RefObj with ties to Foo1 
>>> Foo2 = make_foo_class(2) 
Created RefObj with ties to Foo2 
>>> type(Foo1()), type(Foo2()) 
(<class 'Foo1'>, <class 'Foo2'>) 

también puedo lograrlo con exec, así:

>>> class RefObj(object): 
...  def __init__(self, ParentClassName): 
...   print "Created RefObj with ties to %s" % ParentClassName 
... def make_foo_object(index): 
...  class_template = """class Foo%(index)d(object): 
...   ref_obj = RefObj("Foo%(index)d") 
...   """ % dict(index = index) 
...  global RefObj 
...  namespace = dict(RefObj = RefObj) 
...  exec class_template in namespace 
...  return namespace["Foo%d" % index] 
... 
>>> Foo1 = make_foo_object(1) 
Created RefObj with ties to Foo1 
>>> Foo2 = make_foo_object(2) 
Created RefObj with ties to Foo2 
>>> type(Foo1()), type(Foo2()) 
(<class 'Foo1'>, <class 'Foo2'>) 

El uso de exec no se sienta bien conmigo (como espero que no con mucha gente que lea esta pregunta) pero exec es exactamente cómo python collections.namedtuple() clase is implemented (ver this line). También es muy relevante la defensa de este uso de exechere, por el creador de la clase (Raymond Hettinger). En esta defensa, se afirma que "es una característica clave para las tuplas con nombre que son exactamente equivalentes a una clase manuscrita", lo que podría implicar que el uso de type() no es tan bueno como el uso de exec ...

¿Hay alguna diferencia? ¿Por qué usar exec contra type()?

espero que la respuesta puede ser que ambas formas son lo mismo y es simplemente que la aplicación namedtuple tiene una gran cantidad de variables namedtuple salpicados a través de él, y haciendo esto con generar dinámicamente el cierre de todos los métodos hicieron el código de obtener difícil de manejar , pero quiero saber si hay algo más en esto.

En cuanto a mi incomodidad con exec, reconozco que si no hay forma en que las partes no confiables le inyecten un código nefasto, debería estar bien ... es solo asegurarse de que eso me ponga nervioso.

+1

También hay un poco de buena discusión sobre el tema en http://blog.ccpgames.com/kristjan/2011/05/28/namedtuple-and-exec/, y había otra buena publicación en el blog al respecto No veo ahora. No has presentado ninguna razón por la cual 'type' sea problemático en tu situación, así que no sé por qué te molestarías con' exec' porque sabes que 'type' funciona. (Aparte de la curiosidad obviamente). – agf

+0

@agf - gran enlace, gracias! Originalmente no tuve un problema con 'type', ya que ambos enfoques funcionan. Tenía curiosidad por las diferencias e intentaba entender el motivo del uso de 'exec' en' namedtuple'. Sin embargo, los argumentos de firma de clase/función presentados son excelentes ... Frecuentemente tengo problemas de decorador que requieren el uso del paquete [decorator] (http://pypi.python.org/pypi/decorator). – Russ

+0

¿Podría darnos la razón detrás de la necesidad de creación dinámica de clases o es solo curiosidad? – dhill

Respuesta

2

No hay ninguna desventaja al usar type() sobre exec. Creo que la defensa de Raymond es un poco defensiva. Debe elegir la técnica que le resulte más legible y comprensible. Ambas formas crean un código confuso.

Debería intentar realmente evitar el código que crea clases en primer lugar, sería lo mejor.

+0

@agfL lo siento, no estoy seguro de a qué se refiere. –

+3

Uno de los argumentos para 'exec' para la biblioteca' decorator' era que era más fácil conservar la firma de funciones/métodos decorados. Creo que también fue un argumento para 'namedtuple' que con' exec' las clases eran exactamente las mismas una vez construidas como lo harían si se codificaran a mano, mientras que con 'type' eran diferentes. – agf

+0

Definitivamente me gustaría evitar crear clases sobre la marcha, pero tengo un caso en el que necesito crear atributos de clase basados ​​en parámetros que no puedo garantizar que estén dentro del alcance antes de la creación de la clase. Las clases dinámicas parecen ser las más simples en este momento. – Russ

4

¿Por qué no crear una clase en una función?

def foo_factory(index): 
    name = 'Foo%d' % index 

    class Foo(object): 
     ref_obj = RefObj(name) 

    Foo.__name__ = name 
    return Foo 
+0

Cuando en realidad hay algo _dynamic_ sobre 'Foo' - no desea crearlo básicamente igual cada vez. – agf

+0

Por supuesto, esto es solo un esqueleto. – yak

+0

Por favor, muestre cómo implementaría 'namedtuple' o decoradores que preservan firmas de esta manera. Decir que es "de ninguna manera menos flexible" no es cierto. – agf

7

Recomendaría type sobre exec aquí.

De hecho, la declaración class es sólo azúcar sintáctico para una llamada a type: El cuerpo de la clase se ejecuta dentro de su propio espacio de nombres, que se pasa luego a la metaclase, que por defecto es type si no se especifica metaclase personalizado.

Este enfoque es menos propenso a error ya que no hay necesidad de analizar el código en tiempo de ejecución, y puede ser incluso más rápido.

+0

+1 por practicidad. No merece el -1. – agf

Cuestiones relacionadas