Reputation: 99398
Python in a Nutshell describes the lookup procedure when getting an attribute from a class, e.g. cls.name
and the lookup procedure when getting an attribute from an instance, e.g. obj.name
But I am not sure when it comes to setting an attribute:
Setting an attribute
Note that the attribute lookup steps happen as just described only when you refer to an attribute, not when you bind an attribute. When you bind (on either a class or an instance) an attribute whose name is not special (unless a
__setattr__
method, or the__set__
method of an overriding descriptor, intercepts the binding of an instance attribute), you affect only the__dict__
entry for the attribute (in the class or instance, respectively). In other words, for attribute binding, there is no lookup procedure involved, except for the check for overriding descriptors.
My questions are:
What is the "lookup" procedure when setting an attribute from a class, e.g. cls.name=value
?
What is the "lookup" procedure when setting an attribute from an object, e.g. obj.name=value
?
The procedures seem to involve
the __setattr__
method
__setattr__(self, name, value)
At every request to bind attribute
x.y
(typically, an assignment statementx.y=value
, but also, for example,setattr(x, 'y', value)
), Python callsx.__setattr__('y', value)
. Python always calls__setattr__
for any attribute binding on x—a major diff erence from__getattr__
(__setattr__
is closer to__getattribute__
in this sense). To avoid recursion, whenx.__setattr__
bindsx
’s attributes, it must modifyx.__dict__
directly (e.g., viax.__dict__[name]=value
); even better,__setattr__
can delegate the setting to the superclass (by callingsuper(C, x).__setattr__('y', value)
or, in v3, justsuper().__setattr__('y', value)
). Python ignores the return value of__setattr__
. If__setattr__
is absent (i.e., inherited fromobject
), andC.y
is not an overriding descriptor, Python usually translatesx.y=z
intox.__dict__['y']=z
.
the __set__
method of an overriding descriptor (So I asked Why does `object.__get__` have an argument `owner` for the owner class, while `object.__set__` doesn't?)
__dict__
entry for the attribute (in the class or instance, respectively)Given that the book distinguishes the lookup procedures for getting an attribute from a class and from an instance, it is natural to think that the "lookup" procedures are different when setting an attribute from a class and when setting an attribute from an instance.
But since in Python3, every class object is actually an instance of its metaclass (e.g. type
class), are the "lookup" procedure for setting an attribute from a class and the "lookup" procedure for setting an attribute from an instance really different?
Thanks.
Upvotes: 3
Views: 423
Reputation: 280181
For x.y = z
, Python looks up x.__setattr__
in a way that bypasses __getattribute__
, __getattr__
, and x
's own __dict__
, then calls x.__setattr__('y', z)
.
A custom __setattr__
can do whatever the heck it wants, but here's the default:
Search type(x).__mro__
for a __dict__
entry matching the attribute name.
If this search finds a descriptor descr
with a __set__
method (looking up __set__
the same way we looked up __setattr__
), call descr.__set__(x, z)
.
2.a. In the highly unusual case that the search finds a descriptor with __delete__
but no __set__
, raise an AttributeError
.
Otherwise, set x.__dict__['y'] = z
, or raise an AttributeError
if x
has no __dict__
. (This uses the "real" __dict__
, bypassing things like mappingproxy
.)
This applies to both types and other objects, with the caveats that type.__setattr__
will refuse to set attributes of a class written in C, and type.__setattr__
does some extra internal maintenance you'll never need to worry about unless you do something crazy and unsupported to bypass it.
Upvotes: 2