Reputation: 923
I'm at my wits end. Can't find anything else that helps with this.
dta = {'type': "", 'content': ""}
print dta
>>>{'content': '', 'type': ''}
setattr(dta, 'type', "Steve")
>>>AttributeError: 'dict' object has no attribute 'type'
Upvotes: 4
Views: 11336
Reputation: 231355
setattr
does not work with a dictionary because it does not have an __dict__
.
I can make a simple class and instance:
In [88]: class Obj(object):
...: pass
...:
In [89]: ob = Obj()
In [90]: ob.__dict__
Out[90]: {}
In [91]: setattr(ob,'test',34)
In [92]: ob.__dict__
Out[92]: {'test': 34}
In [95]: ob.test
Out[95]: 34
setattr
sets an item in the __dict__
of obj
, and the resulting attribute can be accessed with .test
syntax.
But a dict
object does not have a __dict__
.
In [96]: dd={1:2, 3:4}
In [97]: dd.__dict__
AttributeError: 'dict' object has no attribute '__dict__'
In [98]: setattr(dd, 'test', 34)
AttributeError: 'dict' object has no attribute 'test'
I add items to the dict with the dictionary indexing notation:
In [99]: dd['test'] = 34
In [100]: dd
Out[100]: {1: 2, 3: 4, 'test': 34}
The dictionary class inherits from object
, but in way that does not give each instance a __dict__
. Thus setattr
has nothing to act on.
If for some reason the []=
syntax is awkward, you can use operator.setitem
as a function:
In [108]: operator.setitem?
Docstring: setitem(a, b, c) -- Same as a[b] = c.
Type: builtin_function_or_method
In [109]: operator.setitem(dd, 'test', 23)
In [110]: dd
Out[110]: {1: 2, 3: 4, 'test': 23}
Or use the __setitem__
method
In [111]: dd.__setitem__('test', 1)
In [112]: dd
Out[112]: {1: 2, 3: 4, 'test': 1}
Actually, a dictionary does have attributes. For example its .get
method can be fetched with getattr
. setattr
returns a different error in this example.
In [119]: getattr(dd,'get')
Out[119]: <function dict.get>
In [120]: setattr(dd,'get',dict.get)
...
AttributeError: 'dict' object attribute 'get' is read-only
Without the __dict__
we can't add an attribute to a dictionary. And many, if not all, of the existing attributes are read only. I don't know if a dictionary has an attributes that can be changed.
Actually a dictionary is an object
:
In [121]: isinstance(dd,dict)
Out[121]: True
In [122]: isinstance(dd,object)
Out[122]: True
I just imported defaultdict
. It too lacks a __dict__
. But I was able to use setattr(ddd,'default_factory', int)
. I think that no attribute
is the correct error message when the attribute does not already exist, and it cannot add new ones.
==============
I'd have to double check the documentation on this, but I think setattr(dd,...)
delegates to dd.__setattr__(...)
.
In [168]: dd.__setattr__('test',1)
...
AttributeError: 'dict' object has no attribute 'test'
In [169]: dd.__setattr__('get',None)
...
AttributeError: 'dict' object attribute 'get' is read-only
So the error message is determined by the dict
class, not setattr
.
Even instances that have a __dict_-
might raise errors if setattr
is used wrong. For example a number is not a valid attribute name:
In [171]: setattr(ob, 123, 'a')
....
TypeError: attribute name must be string, not 'int'
But setattr
can set attributes that can't be accessed with the .
syntax:
In [172]: setattr(ob, 'a#re',1)
In [174]: ob.a#re
...
AttributeError: 'Obj' object has no attribute 'a'
In [175]: getattr(ob, 'a#re')
Out[175]: 1
vars()
is another way of accessing the __dict__
of an instance:
In [179]: vars(dd)
...
TypeError: vars() argument must have __dict__ attribute
Upvotes: 6
Reputation: 23743
Dictionaries are subscriptable, the key:value pairs are items instead of attributes.
Items are accessed using a subscript notation:
>>> d = {'a':1, 'b':2}
>>> d['a']
1
Attributes are accessed using dot notation
>>> d.a
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
d.a
AttributeError: 'dict' object has no attribute 'a'
operator.itemgetter is similar to getattr:
>>> import operator
>>> bee = operator.itemgetter('b')
>>> bee(d)
2
>>>
>>> hasattr(d, '__getitem__')
True
>>> hasattr(d, '__getattr__')
False
>>>
Upvotes: 1
Reputation: 923
docs.python.org/2/library/functions.html#setattr
This is the counterpart of getattr(). The arguments are an object, a string and an arbitrary value. The string may name an existing attribute or a new attribute. The function assigns the value to the attribute, provided the object allows it
OBJECT is not a DICTIONARY. And I probably confusing my JS work (syntax) with the Python code I'm always writing at the same time
So the error message is confusing:
AttributeError: 'dict' object has no attribute 'type'
It should be more like:
'setattr() is for objects not dictionaries'
.
Thanks everyone.
Upvotes: 0
Reputation: 6237
Python's dictionaries aren't JS objects. When you create a dict, you aren't creating a dynamic object whose properties you can change at runtime as in JS. Instead the dictionary knows how to store pairs of keys and values and does so by overriding the operator [] (def __getitem__(self, key)
).
On a more implementation level - calling getattr
/ setattr
is really a shorthand for data.__getattr__("foo")
and since dict
uses __getitem__
as opposed to __getattr__
the function call fails.
Thus, there's no way to set (or get for that matter) the dict's items using generic attribute functions.
However, you can create your custom dict class that does support that operation (although I wouldn't recommend it):
class AttrDict(dict):
def __init__(self):
dict.__init__(self)
# Override getattr and setattr so that they return the values of getitem / setitem
def __setattr__(self, name, value):
self[name] = value
def __getattr__(self, name):
return self[name]
data = AttrDict()
data["foo"] = "bar"
print(getattr(data, "foo"))
Upvotes: 9
Reputation: 395
'setattr()' refers to something else. When you write setattr(dta, 'type', "Steve")
you're trying to access the field dta.type
, dict class has no attribute type
, so it gives an error.
dict_object['key']
is a completely different thing, and it's how dict members should be accessed.
More about settatr() Here
Upvotes: 1
Reputation: 18106
You can assign dict values directly, setattr() is not needed.
dta = {'type': "", 'content': ""}
dta["type"] = "Steve"
Upvotes: 2