Why use setattr() and getattr() built-ins?
Question:
Answers:
Because you can use a dynamic variable too:
somevar = 'foo'
getattr(x, somevar)
You can’t do that with regular attribute access syntax.
Note that getattr()
also takes an optional default value, to be returned if the attribute is missing:
>>> x = object()
>>> getattr(x, 'foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'foo'
>>> getattr(x, 'foo', 42)
42
Using getattr()
you can pull the attribute name from something else, not a literal:
for attrname in dir(x):
print('x.{} = {!r}'.format(attrname, getattr(x, attrname))
or you can use setattr()
to set dynamic attributes:
for i, value in enumerate(dynamic_values):
setattr(i, 'attribute{}'.format(i), value)
You use them if the attribute you want to access is a variable and not a literal string. They let you parameterize attribute access/setting.
There’s no reason to do getattr(x, 'foobar')
, but you might have a variable called attr
that could be set to “foobar” or “otherAttr”, and then do getattr(x, attr)
.
I find it most useful when there is a possibility that the object whose attribute you need might be None
. Exemplified below;
obj = None
attr_val = getattr(obj, 'anyvar', None) #This is not an error.
Another case probably shows that they are not totally identical:
class A:
def __init__(self):
self.__var = 10
setattr(self, '__var2', 100)
a = A()
print(a.__var2)
# 100
print(a.__var)
# AttributeError: 'A' object has no attribute '__var'
At least, setattr
is not identical to .
.
There is a difference between setattr
and .
, not documented yet:
class X:
def __init__(self, value1, value2):
self.__non_private_name_1 = value1
setattr(self, '__non_private_name_2', value2)
>>> x = X('Hi', 'Bye')
>>> x.__dict__
{'_X__non_private_name_1': 'Hi', '__non_private_name_2': 'Bye'}
The latter one, when used to set a dunder value (ones with double underscores) adds a single underscore _
+ self.__class__.__name__
(i.e. X
) to the beginning of the string in right side of .
and sets the attribute named with the resulting string(i.e. _X__non_private_name_1
).
Because you can use a dynamic variable too:
somevar = 'foo'
getattr(x, somevar)
You can’t do that with regular attribute access syntax.
Note that getattr()
also takes an optional default value, to be returned if the attribute is missing:
>>> x = object()
>>> getattr(x, 'foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'foo'
>>> getattr(x, 'foo', 42)
42
Using getattr()
you can pull the attribute name from something else, not a literal:
for attrname in dir(x):
print('x.{} = {!r}'.format(attrname, getattr(x, attrname))
or you can use setattr()
to set dynamic attributes:
for i, value in enumerate(dynamic_values):
setattr(i, 'attribute{}'.format(i), value)
You use them if the attribute you want to access is a variable and not a literal string. They let you parameterize attribute access/setting.
There’s no reason to do getattr(x, 'foobar')
, but you might have a variable called attr
that could be set to “foobar” or “otherAttr”, and then do getattr(x, attr)
.
I find it most useful when there is a possibility that the object whose attribute you need might be None
. Exemplified below;
obj = None
attr_val = getattr(obj, 'anyvar', None) #This is not an error.
Another case probably shows that they are not totally identical:
class A:
def __init__(self):
self.__var = 10
setattr(self, '__var2', 100)
a = A()
print(a.__var2)
# 100
print(a.__var)
# AttributeError: 'A' object has no attribute '__var'
At least, setattr
is not identical to .
.
There is a difference between setattr
and .
, not documented yet:
class X:
def __init__(self, value1, value2):
self.__non_private_name_1 = value1
setattr(self, '__non_private_name_2', value2)
>>> x = X('Hi', 'Bye')
>>> x.__dict__
{'_X__non_private_name_1': 'Hi', '__non_private_name_2': 'Bye'}
The latter one, when used to set a dunder value (ones with double underscores) adds a single underscore _
+ self.__class__.__name__
(i.e. X
) to the beginning of the string in right side of .
and sets the attribute named with the resulting string(i.e. _X__non_private_name_1
).