2010-07-20 15 views
15

Ho una classe Python con attributi denominati: date1, date2, date3, ecc.Come comporre e accedere dinamicamente agli attributi di classe in Python?

Durante il runtime, ho una variabile i, che è un numero intero.

Quello che voglio fare è accedere all'attributo data appropriato in fase di esecuzione in base al valore di i.

Per esempio,

se i == 1, voglio accedere myobject.date1

se i == 2, voglio accedere myobject.date2

E io voglio fare qualcosa di simile per la classe anziché l'attributo.

Ad esempio, ho un sacco di classi: MyClass1, MyClass2, MyClass3, ecc. E ho una variabile k.

se k == 1, voglio creare un'istanza di una nuova istanza di MyClass1

se k == 2, voglio creare un'istanza di una nuova istanza di MyClass2

Come posso fare questo?

EDIT

spero di evitare l'uso di un gigante if-then-else per selezionare l'appropriato attributo/classe.

C'è un modo in Python per comporre il nome della classe al volo usando il valore di una variabile?

+2

Si prega di modificare questa domanda. Si prega di leggere i suggerimenti di formattazione sul lato destro della pagina. Si prega di formattare il codice correttamente. –

+1

Si prega di formattare correttamente questa domanda. Voglio sostituirlo come una domanda utile, poiché si tratta di un bell'esempio di metaprogrammazione e introspezione. Ma ha bisogno di editing. –

risposta

19

È possibile utilizzare getattr() di accedere a una proprietà quando non lo fai conoscere il suo nome fino al runtime:

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

Se tenete gli classi numerate in un modulo chiamato foo, è possibile utilizzare getattr() nuovo per accedervi in ​​base al numero.

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)() 

Detto tutto ciò, è davvero dovrebbe evitare questo genere di cose perché si sarà mai in grado di scoprire dove queste proprietà e classi numerati vengono utilizzati se non per la lettura attraverso la vostra intera base di codice. È meglio mettere tutto in un dizionario.

+4

Devo enfatizzare l'ultimo paragrafo con un grande pennarello rosso. Ci sono 2+ identificatori con lo stesso nome e un numero progressivo alla fine? Refactor it. Cioè, a meno che tu non abbia una ragione che è il doppio del necessario per essere considerata una buona ragione. – delnan

+0

Completamente d'accordo. Data una scelta, non lascerei mai che il codice come quello passi la recensione. – Daenyth

0

Per il primo esempio, aggiungerei un metodo di istanza sulla classe.

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

Per la seconda userei una funzione di fabbrica:

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

    # etc... 
+0

Beh, sto cercando di evitare di avere una gigantesca dichiarazione if-then-else. – Continuation

4

OK, beh ... sembra che questo ha bisogno di un po 'di lavoro. In primo luogo, per la tua data * cose, dovrebbero forse essere memorizzate come un dittico di attributi. ad esempio, myobj.dates[1], così via.

Per le classi, sembra che tu voglia il polimorfismo. Tutte le tue classi MyClass * dovrebbero avere un antenato comune. Il metodo __new__ dell'antenato dovrebbe determinare quale dei suoi figli istanziare.

Un modo per il genitore di sapere cosa fare è tenere un ordine dei bambini. Ci sono modi in cui la classe genitore non ha bisogno di enumerare i suoi figli cercando tutte le sue sottoclassi, ma è un po 'più complessa da implementare. Vedere here per ulteriori informazioni su come si potrebbe adottare tale approccio. Leggi i commenti in particolare, si espandono su di esso.

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 

In terzo luogo, è davvero necessario riconsiderare la denominazione delle variabili. Non utilizzare i numeri per enumerare le variabili, ma dare loro nomi significativi. i e k sono pessimi da utilizzare in quanto sono riservati per gli indici di ciclo.

Un esempio del codice esistente sarebbe molto utile per migliorarlo.

+0

In che modo il metodo __new__ dell'antenato individua quali bambini istanziare senza ricorrere a una dichiarazione if-then-else? Spero di usare il valore della variabile per "comporre" direttamente il nome della classe al volo. È possibile in Python? – Continuation

+0

@Continuazione: ho aggiornato la mia risposta per includere ulteriori informazioni – Daenyth

+0

Ho il caso in cui gli attributi nameInfo, groupInfo ecc. Provengono da vSphere api, e cosa accedere a quelli dinamicamente/programmaticamente in un ciclo iterativo, quindi il dizionario non lo fa t abbastanza lavoro per me – Hvisage

5

Per il primo caso, si dovrebbe essere in grado di fare:

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

Per il secondo caso, si può fare:

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

Tuttavia, il fatto che è necessario fare questo in il primo posto può essere un segno che ti stai avvicinando al problema in un modo molto non-Pythonic.

2

Concordo con Daenyth, ma se ti senti impertinente è possibile utilizzare il metodo didict che viene fornito con tutte le classi:

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

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

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

per ottenere un elenco di tutti gli attributi, provare:

dir(<class instance>) 
Problemi correlati