I have Python 2.7 code that uses defaultdict
to create a dictionary mydict
that creates default objects of MyClass
, as shown below.
My understanding is that if the key foo
does not exist in the defaultdict, then a new object of MyClass
would be created, and since I have an __init__()
method, then the attribute value
would also exist. So why doesn't the code below work, where I have:
print mydict['foo'].value # Raises AttributeError
However, this code does work:
mydict['foo'].value = 1 # Works fine
What's the correct approach to solve the error? (I'm a C++/Java programmer, by the way.)
Below is the entirety of the code:
from collections import defaultdict
class MyClass(object):
def __init__(self):
self.value = 0
mydict = defaultdict(lambda: MyClass)
print mydict['foo'].value # Raises AttributeError
#mydict['foo'].value += 1 # Raises AttributeError
#mydict['foo'].value = 1 # Works fine
The result:
AttributeError Traceback (most recent call last)
<ipython-input-16-c9dd776d73de> in <module>()
6
7 mydict = defaultdict(lambda: MyClass)
----> 8 print mydict['foo'].value # Raises AttributeError
9 #mydict['foo'].value += 1 # Raises AttributeError
10 #mydict['foo'].value = 1 # Works fine
AttributeError: type object 'MyClass' has no attribute 'value'
When you specify defaultdict(lambda: MyClass)
, what the default dict produces is the MyClass
definition (through the lambda) which does not have value
as it is not defined in the class, as that is what is being returned with that particular construct (default dict will invoke the first argument that it was constructed with if it was a callable object, and you can see this yourself already in the exception message you have included in your question). See this example:
>>> MyClass.value
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'MyClass' has no attribute 'value'
>>> mydict['foo'].value = 1
>>> MyClass.value
1
If you truly want to return the raw class, provide that value attribute like so (but it will be shared across all default values)
class MyClass(object):
value = None
Or do what you really want, which is to provide a default instance of the class, which you can call that manually in the lambda, or reduce it to simply:
mydict = defaultdict(MyClass)