Python's hasattr on list values of dictionaries always returns false?
Solution 1
hasattr
does not test for members of a dictionary. Use the in
operator instead, or the .has_key
method:
>>> example = dict(foo='bar')
>>> 'foo' in example
True
>>> example.has_key('foo')
True
>>> 'baz' in example
False
But note that dict.has_key()
has been deprecated, is recommended against by the PEP 8 style guide and has been removed altogether in Python 3.
Incidentally, you'll run into problems by using a mutable class variable:
>>> class example(object):
... foo = dict()
...
>>> A = example()
>>> B = example()
>>> A.foo['bar'] = 'baz'
>>> B.foo
{'bar': 'baz'}
Initialize it in your __init__
instead:
class State(object):
info = None
def __init__(self):
self.info = {}
Solution 2
A dictionary key is not the same as an object attribute
thing1 = {'a', 123}
hasattr(thing1, 'a') # False
class c: pass
thing2 = c()
thing2.a = 123
hasattr(thing2, 'a') # True
Solution 3
To test for elements in a list/dictionary, use in
. To use defaults, you can use dict.get
:
def add_to_info(self, key_string, integer):
array = self.info.get(key_string, [])
array.append(integer)
self.info[key_string] = array
Or use 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)
Solution 4
Looks like all you need is one line:
def add_to_info(self, key_string, integer):
self.info.setdefault(key_string, []).append(integer)
Chris Keele
Tech tinkerer, web enveloper, professional digresser.
Updated on April 02, 2020Comments
-
Chris Keele about 4 years
I have a dictionary that sometimes receives calls for non-existent keys, so I try and use
hasattr
andgetattr
to handle these cases: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
The first time this runs with
integer = 1
:current info: {} False [] current info: {'foo': [1]}
Running this code again with
integer = 2
:instance.add_to_info("foo", 2) current info: {'foo': [1]} False [] current info: {'foo': [2]}
The first run is clearly successful (
{'foo': [1]}
), buthasattr
returns false andgetattr
uses the default blank array the second time around, losing the value of1
in the process! Why is this? -
Chris Keele almost 12 yearsI'll be damned! I thought I was going crazy. I've always used the
if/in/else
structure for these dictionary testing situations with default values. This time since the actual situation is actually much more intricate than my example, with a few other hasattrs flying around, I favored that "syntax" instead... which obviously doesn't work! I'll remember this gotcha from now on, thanks! -
Chris Keele almost 12 yearsAlso, I'm dealing with info in init appropriately, just wanted to streamline the example.
-
Chris Smith almost 5 yearsAh. This was driving me mad. Thanks for sharing example.