According to this answer:
setattr(instance, name, value)
is syntactic sugar forinstance.__setattr__(name, value)
But:
class C: def __init__(self): # OK. super().__setattr__("foo", 123) # AttributeError: 'super' object has no attribute 'foo' setattr(super(), "foo", 123) c = C()
What gives? Shouldn’t they both do the same thing?
Advertisement
Answer
The answer you linked to glosses over some important details. Long story short, setattr
bypasses super
‘s magic, so it tries to set attributes on the super()
proxy object itself instead of on self
.
setattr(a, b, c)
is not syntactic sugar for a.__setattr__(b, c)
. setattr
and regular attribute assignment both look for a __setattr__
method through a direct search of an object’s type’s method resolution order (a carefully-ordered list of the class and all superclasses), bypassing both the instance dict and __getattribute__
/__getattr__
hooks.
In contrast, a.__setattr__(b, c)
just performs a regular attribute lookup for __setattr__
, so it goes through __getattribute__
and __getattr__
, and it checks the instance dict unless __getattribute__
says not to.
super
implements a custom __getattribute__
method to do its attribute magic. super().__setattr__
uses this hook, finding C
‘s superclass’s __setattr__
and binding self
, resulting in a method object for setting attributes on self
.
setattr(super(), ...)
bypasses __getattribute__
. It performs the direct MRO search mentioned previously, searching through super
‘s MRO, resulting in a method object for setting attributes on the super instance itself. The super
instance doesn’t have a __dict__
to store arbitrary attribute data in, so trying to set a foo
attribute fails with a TypeError
.