I'm trying to update an attribute of a class with setattr
and a string. So for instance with this code:
class ClassA():
def __init__(self):
self.A = 0
class ClassB():
def __init__(self):
self.CA = ClassA()
CB = ClassB()
setattr(CB, "CA.A", 2)
print(CB.CA.A)
when I do setattr(CB, "CA.A", 2)
, it doesn't update the attribute A
of the CA
class inCB
. Instead, it creates another attribute, as in the picture:
Then when I print print(CB.CA.A)
, I get a 0 value.
I don't understand why this happend and if a solution exists?
setattr()
takes a literal attribute name. It does not support nested attributes; the .
in the name is just a dot in the name, not an attribute separator.
Put differently, .
in CB.CA.A = 2
is syntax to access attributes, not part of the attribute name. CB.CA
is looked up first, and then the assignment takes place to the A
attribute on the result.
You need to split on the .
and use a separate getattr()
call to obtain the parent object first:
setattr(getattr(CB, 'CA'), "A", 2)
You could wrap that logic into a function:
def setattr_nested(base, path, value):
"""Accept a dotted path to a nested attribute to set."""
path, _, target = path.rpartition('.')
for attrname in path.split('.'):
base = getattr(base, attrname)
setattr(base, target, value)