2012-11-28 23 views
14

ho e oggetto con un pseudo o speciale attributo che può essere nominato in tre modi diversi (Nota: Non controllare il codice che genera l'oggetto)ottenere attributo dinamico in python

Il valore negli attributi (a seconda che uno è impostato) è esattamente la stessa, e ho bisogno di ottenere che per l'ulteriore elaborazione, quindi, a seconda della fonte di dati, posso avere qualcosa di simile:

>>> obj.a 
'value' 
>>> obj.b 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: Obj instance has no attribute 'b' 
>>> obj.c 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: Obj instance has no attribute 'c' 

o

>>> obj.a 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: Obj instance has no attribute 'a' 
>>> obj.b 
'value' 
>>> obj.c 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: Obj instance has no attribute 'c' 

o

>>> obj.a 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: Obj instance has no attribute 'a' 
>>> obj.b 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: Obj instance has no attribute 'b' 
>>> obj.c 
'value' 

Sono interessato a ottenere 'value' e purtroppo __dict__ proprietà non esiste in tale oggetto. Quindi quello che ho finito di fare per ottenere quel valore era fare un bel po 'di chiamate getattr. Supponendo che le possibilità sono solo tre, il codice si presentava così:

>>> g = lambda o, l: getattr(o, l[0], getattr(o, l[1], getattr(o, l[2], None))) 
>>> g(obj, ('a', 'b', 'c')) 
'value' 

Ora, vorrei sapere se c'è un modo migliore di questo? Come io sono convinto al 100% quello che ho fatto :)

Grazie in anticipo

+0

Solo per controllare - non c'è un 'get_value()' (o simile) o come si menziona 'a seconda della fonte di dati' - nessuna mappatura esistente (o derivabile) di fonte -> nome attributo? –

risposta

28

ne dite:

for name in 'a', 'b', 'c': 
    try: 
     thing = getattr(obj, name) 
    except AttributeError: 
     pass 
    else: 
     break 
+0

In realtà stavo cercando di evitare un tentativo/eccetto in questo .. avrei dovuto dire che :) – rhormaza

+1

prova/eccetto è il tipico modo pitonico, qualsiasi buon motivo hai voluto evitarlo? – wim

+0

Non c'è una buona ragione, volevo solo mantenere il codice conciso e ho provato un tentativo/eccetto un po 'troppo :) L'altra cosa che stavo verificando ora è che il blocco non fornisce un valore predefinito in caso di nessuna queste proprietà esistono, sebbene sia altamente improbabile, tuttavia penso che sia sempre opportuno utilizzare un approccio difensivo :) – rhormaza

0

penso utilizzando dir otterrà u essenzialmente la stessa cosa __dict__ fa normalmente ...

targetValue = "value" 
for k in dir(obj): 
    if getattr(obj,k) == targetValue: 
     print "%s=%s"%(k,targetValue) 

qualcosa come

>>> class x: 
... a = "value" 
... 
>>> dir(x) 
['__doc__', '__module__', 'a'] 
>>> X = x() 
>>> dir(X) 
['__doc__', '__module__', 'a'] 
>>> for k in dir(X): 
...  if getattr(X,k) == "value": 
...  print "%s=%s"%(k,getattr(X,k)) 
... 
a=value 
>>> 
+0

Grazie per questo, ho completamente perso dir() ... mi chiedo ora ... come funziona la chiamata dir() internamente. Controllerò – rhormaza

2

Questo ha il vantaggio di lavorare con un numero qualsiasi di elementi:

def getfirstattr(obj, *attrs): 
    return next((getattr(obj, attr) for attr in attrs 
        if hasattr(obj, attr)), None) 

Ciò ha l'inconveniente molto minore che fa due ricerche sul valore finale: una volta per verificare che l'attributo esiste, un'altra per ottenere effettivamente il valore. Questo può essere evitato usando un'espressione di generatore annidato:

def getfirstattr(obj, *attrs): 
    return next((val for val in (getattr(obj, attr, None) for attr in attrs) 
        if val is not None), None) 

Ma non mi sembra davvero un grosso problema. Probabilmente l'espressione del generatore sarà più veloce di un semplice ciclo vecchio anche con la doppia ricerca.

+1

ho quasi postato qualcosa a questo effetto. il problema è che finisci per controllare due volte l'elemento che vuoi (una volta con 'hasattr' e una volta con' getattr'). Alla fine, ho deciso che non era molto meglio del ciclo esplicito postato da wim - Anche se sono aperto a conoscere i vantaggi che questo potrebbe avere nel ciclo. – mgilson

+0

Si potrebbe usare un generatore annidato per evitare la doppia ricerca, credo, ma succede solo quando trova l'attributo, quindi lo considero un piccolo svantaggio. Ho appena aggiunto una versione del genere, comunque. – kindall

+0

nice one ... Ho intenzione di dare un'occhiata ... grazie – rhormaza

0

Potrebbe esserci un modo migliore per farlo a seconda della struttura del tuo oggetto, ma non sapendo nient'altro, ecco una soluzione ricorsiva che funziona esattamente come la tua attuale soluzione eccetto che funzionerà con un numero arbitrario di argomenti:

g = lambda o, l: getattr(o, l[0], g(o, l[1:])) if l else None 
+0

Mi è piaciuto molto questo, per la semplicità e la brevità – rhormaza

-1

E un altro:

reduce(lambda x, y:x or getattr(obj, y, None), "a b c".split(), None) 

(in Python 3 è necessario importare ridurre da functools.È incorporato in Python 2)

+0

Questo presuppone che 'bool (obj.b)' sia True. Considera cosa succederebbe qui se tu avessi 'obj.a = 0'. Non penso che lo raccoglieresti. – mgilson

Problemi correlati