2012-05-23 17 views
14

Ho un dizionario che a volte riceve le chiamate per i tasti inesistenti, così ho cercare di utilizzare hasattr e getattr per gestire questi casi:L'hasattr di Python sui valori di lista dei dizionari restituisce sempre false?

key_string = 'foo' 
print "current info:", info 
print hasattr(info, key_string) 
print getattr(info, key_string, []) 
if hasattr(info, key_string): 
    array = getattr(info, key_string, []) 
array.append(integer) 
info[key_string] = array 
print "current info:", info 

La prima volta che questo viene eseguito con integer = 1:

current info: {} 
False 
[] 
current info: {'foo': [1]} 

l'esecuzione di questo codice di nuovo con integer = 2:

instance.add_to_info("foo", 2) 

current info: {'foo': [1]} 
False 
[] 
current info: {'foo': [2]} 

La prima esecuzione ha chiaramente successo ({'foo': [1]}), ma restituisce false e getattr utilizza la matrice vuota predefinita la seconda volta, perdendo il valore di 1 nel processo! Perchè è questo?

risposta

21

hasattr non testare membri di un dizionario. Utilizzare l'operatore in invece, o il metodo .has_key:

>>> example = dict(foo='bar') 
>>> 'foo' in example 
True 
>>> example.has_key('foo') 
True 
>>> 'baz' in example 
False 

Ma si noti che dict.has_key() è sconsigliata, è raccomandato dalla guida contro stile PEP 8 ed è stato rimosso completamente in Python 3.

Incidentalmente, si incorrerà in problemi utilizzando una variabile di classe mutevole:

>>> class example(object): 
...  foo = dict() 
... 
>>> A = example() 
>>> B = example() 
>>> A.foo['bar'] = 'baz' 
>>> B.foo 
{'bar': 'baz'} 

inizializzare nel vostro __init__ invece:

class State(object): 
    info = None 

    def __init__(self): 
     self.info = {} 
+0

Sarò dannato! Pensavo stavo impazzendo. Ho sempre usato la struttura 'if/in/else' per queste situazioni di test del dizionario con valori predefiniti. Questa volta dal momento che la situazione attuale è in realtà molto più complessa del mio esempio, con pochi altri hasattr che volano in giro, ho preferito quella "sintassi" invece ... che ovviamente non funziona! Mi ricorderò di questo, d'ora in poi, grazie! –

+0

Inoltre, ho a che fare con informazioni in init in modo appropriato, volevo solo semplificare l'esempio. –

2

Una chiave dizionario non è la stessa di attributo oggetto

thing1 = {'a', 123} 
hasattr(thing1, 'a') # False 
class c: pass 
thing2 = c() 
thing2.a = 123 
hasattr(thing2, 'a') # True 
1

Per verificare elementi in un elenco/dizionario, utilizzare in. Per utilizzare le impostazioni predefinite, è possibile utilizzare dict.get:

def add_to_info(self, key_string, integer): 
    array = self.info.get(key_string, []) 
    array.append(integer) 
    self.info[key_string] = array 

Oppure utilizzare defaultdict:

from collections import defaultdict 
class State(object): 
    info = defaultdict(list) 

    def add_to_info(self, key_string, integer): 
     self.info[key_string].append(integer) 
2

Sembra che tutto ciò che serve è una sola riga:

def add_to_info(self, key_string, integer): 
    self.info.setdefault(key_string, []).append(integer) 
0

E 'comodo avere definito un riflettente getAttr che ottiene attributi o chiavi da un oggetto.

def getAttr(obj, attribute, default=''): 

    # like getattr, but also check the keys of obj, and can return a default, if no key or no attribute was found. 
    # note there's a priority to attribute if both attribute and key exist. 

    result = getattr(obj, attribute) if hasattr(obj, attribute) else None 
    if result is None: 
    result = obj.get(attribute, default) if isinstance(obj, dict) else default 
    return result