ymmx
ymmx

Reputation: 4967

setattr doesn't update the correct attribute of a class when using string

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:

enter image description here in the class CB named CA.A

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?

Upvotes: 0

Views: 2299

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1124328

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)

Upvotes: 5

Related Questions