Tim
Tim

Reputation: 99398

What is the lookup procedure when setting an attribute from a class or from an instance?

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:

The procedures seem to involve

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

Answers (1)

user2357112
user2357112

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:

  1. Search type(x).__mro__ for a __dict__ entry matching the attribute name.

  2. 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.

  3. 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

Related Questions