2012-05-23 11 views
14

que tienen un diccionario que a veces recibe llamadas para llaves inexistentes, por lo que tratar de utilizar hasattr y getattr para manejar estos casos:Python's hasattr on list values ​​of dictionaries siempre devuelve 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 primera vez que esto se ejecuta con integer = 1:

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

la ejecución de este código de nuevo con integer = 2:

instance.add_to_info("foo", 2) 

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

La primera ejecución es claramente exitosa ({'foo': [1]}), pero hasattr devuelve falso y getattr utiliza la matriz en blanco por defecto la segunda vez, perdiendo el valor de 1 en el proceso. ¿Por qué es esto?

Respuesta

21

hasattr no prueba para los miembros de un diccionario. Utilice el operador in lugar, o el método .has_key:

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

Pero tenga en cuenta que dict.has_key() ya no se utiliza, se recomienda contra la guía de estilo PEP 8 y ​​ha sido eliminado por completo en Python 3.

Por cierto, se encontrará con problemas mediante el uso de una variable de clase mutable:

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

inicializarlo en su lugar __init__:

class State(object): 
    info = None 

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

I'll be damned! Pensé que me estaba volviendo loco. Siempre he usado la estructura 'if/in/else' para estas situaciones de prueba de diccionario con valores predeterminados. Esta vez, dado que la situación real es en realidad mucho más intrincada que mi ejemplo, con algunos otros obstáculos volando, preferí esa "sintaxis" ... ¡lo que obviamente no funciona! Lo recordaré de ahora en adelante, ¡gracias! –

+0

Además, estoy tratando con información en init de manera apropiada, solo quería simplificar el ejemplo. –

2

Una de las claves del diccionario no es lo mismo que un atributo de objeto

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

Para probar elementos en una lista/diccionario, use in. Para utilizar los valores predeterminados, puede utilizar dict.get:

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

O utilice 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

Parece que todo lo que necesita es una línea:

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

Es útil tener definido un reflexivo getAttr que obtiene atributos o claves de un objeto.

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 
Cuestiones relacionadas