2010-07-20 20 views
15

Tengo una clase de Python que tiene atributos llamados: date1, date2, date3, etc.¿Cómo redactar y acceder dinámicamente a los atributos de clase en Python?

Durante el tiempo de ejecución, tengo una variable i, que es un número entero.

Lo que quiero hacer es acceder al atributo de fecha apropiado en el tiempo de ejecución en función del valor de i.

Por ejemplo,

si i == 1, quiero acceder myobject.date1

si i == 2, quiero acceder myobject.date2

Y yo quiero hacer algo similar para clase en lugar de atributo.

Por ejemplo, tengo un montón de clases: MyClass1, MyClass2, MyClass3, etc. Y tengo una variable k.

si k == 1, Quiero una nueva instancia de MyClass1

si k == 2, Quiero una nueva instancia de miclase2

¿Cómo puedo hacer eso?

EDITAR

Estoy esperando para evitar el uso de un gigante if-then-else para seleccionar el atributo/clase apropiada.

¿Existe alguna manera en Python para componer el nombre de la clase sobre la marcha utilizando el valor de una variable?

+2

favor editar esta pregunta. Lea las sugerencias de formato en el lado derecho de la página. Por favor, formatee su código correctamente. –

+1

Por favor formatee esta pregunta correctamente. Quiero subirlo como una pregunta útil, ya que este es un buen ejemplo de metaprogramación e introspección. Pero necesita edición. –

Respuesta

19

Puede utilizar getattr() acceder a una propiedad cuando no lo hace conocer su nombre hasta tiempo de ejecución:

obj = myobject() 
i = 7 
date7 = getattr(obj, 'date%d' % i) # same as obj.date7 

Si mantiene sus clases numeradas en un módulo llamado foo, puede utilizar getattr() de nuevo para acceder a ellos por número.

foo.py: 
    class Class1: pass 
    class Class2: pass 
    [ etc ] 


bar.py: 
    import foo 
    i = 3 
    someClass = getattr(foo, "Class%d" % i) # Same as someClass = foo.Class3 
    obj = someClass() # someClass is a pointer to foo.Class3 
    # short version: 
    obj = getattr(foo, "Class%d" % i)() 

Habiendo dicho todo esto, que realmente deben evitar este tipo de cosas, porque nunca será capaz de averiguar dónde se están utilizando estas propiedades numeradas y las clases, excepto por la lectura a través de todo su código base. Es mejor que pongas todo en un diccionario.

+4

Tengo que enfatizar ese último párrafo con un gran marcador rojo. ¿Hay más de 2 identificadores con el mismo nombre y un número en ejecución al final? Refactorizarlo. Es decir, a menos que tengas una razón que sea el doble de buena para ser considerada una buena razón. – delnan

+0

completamente acordado. Si tuviera una opción, nunca dejaría que el código revisara ese pase. – Daenyth

0

Para el primer ejemplo, agregaría un método de instancia en la clase.

def get_date_for_i(self, i): 
    if i == 1: 
     return self.date1 
    elif i == 2: 
     return self.date2 
    # etc... 

Por segundo que haría uso de una función de fábrica:

def get_class_for_k(k): 
    if k == 1: 
     return MyClass1 
    if k == 2: 
     return MyClass2 

    # etc... 
+0

Bueno, estoy tratando de evitar tener una declaración gigante if-then-else. – Continuation

4

OK, bueno ... parece que este necesita un poco de trabajo. En primer lugar, para las cosas de su fecha *, deberían almacenarse tal vez como un dict de atributos. por ejemplo, myobj.dates[1], etc.

Para las clases, parece que quieres polimorfismo. Todas sus clases de MyClass * deben tener un antecesor común. El método __new__ del ancestro debería averiguar cuál de sus hijos crear una instancia.

Una forma de que el padre sepa qué hacer es mantener un dict de los niños. Hay formas en que la clase principal no necesita enumerar sus hijos buscando todas sus subclases, pero es un poco más complejo de implementar. Consulte here para obtener más información sobre cómo puede tomar ese enfoque. Lea los comentarios especialmente, ellos lo amplían.

class Parent(object): 
    _children = { 
     1: MyClass1, 
     2: MyClass2, 
    } 

    def __new__(k): 
     return object.__new__(Parent._children[k]) 

class MyClass1(Parent): 
    def __init__(self): 
     self.foo = 1 

class MyClass2(Parent): 
    def __init__(self): 
     self.foo = 2 

bar = Parent(1) 
print bar.foo # 1 
baz = Parent(2) 
print bar.foo # 2 

En tercer lugar, realmente debería reconsiderar su nomenclatura variable. No use números para enumerar sus variables, en cambio déles nombres significativos. i y k son malos de usar, ya que son por convención reservados para los índices de bucle.

Una muestra de su código actual sería muy útil para mejorarlo.

+0

¿De qué manera el método __nuevo__ del ancestro averiguaría qué niños crear una instancia sin tener que recurrir a una gran declaración if-then-else? Espero utilizar el valor de la variable para "componer" directamente el nombre de la clase sobre la marcha. ¿Es eso posible en Python? – Continuation

+0

@Continuation: actualicé mi respuesta para incluir más información – Daenyth

+0

Tengo el caso donde los atributos nameInfo, groupInfo etc. provienen de vSphere api, y de qué acceder dinámicamente/programáticamente en un ciclo iterativo, entonces el diccionario no funciona Trabajo bastante para mí – Hvisage

5

Para el primer caso, usted debe ser capaz de hacer:

getattr(myobject, 'date%s' % i) 

Para el segundo caso, se puede hacer:

myobject = locals()['MyClass%s' % k]() 

Sin embargo, el hecho de que usted tiene que hacer esto en el primer lugar puede ser una señal de que estás abordando el problema de una manera no muy pitonica.

2

Estoy de acuerdo con Daenyth, pero si te sientes atrevida puede utilizar el método dedict que viene con todas las clases:

>>> class nullclass(object): 
     def nullmethod(): 
      pass 

>>> nullclass.__dict__.keys() 
['__dict__', '__module__', '__weakref__', 'nullmethod', '__doc__'] 

>>> nullclass.__dict__["nullmethod"] 
<function nullmethod at 0x013366A8> 
1

para obtener una lista de todos los atributos, probar:

dir(<class instance>) 
Cuestiones relacionadas