On Mon, Dec 17, 2018, 12:09 PM Paul Baker <paulbak...@gmail.com wrote: > When Python looks up an attribute on an object (i.e. when it executes > `o.a`), it uses an interesting priority order [1]. It looks for: > > 1. A class attribute that is a data-descriptor (most commonly a property) > 2. An instance attribute > 3. Any other class attribute > > We can confirm this using the code below, which creates an object `o` > with an instance attribute `a`, whose class contains a property of the > same name: > > class C: > def __init__(self): > self.__dict__['a'] = 1 > > @property > def a(self): > return 2 > > o = C() > print(o.a) # Prints 2 > > Why does Python use this priority order rather than the "naive" order > (instance attributes take priority over all class attributes, as used > by JavaScript)? Python's priority order has a significant drawback: it > makes attribute lookups slower, because instead of just returning an > attribute of `o` if it exists (a common case), Python must first > search `o`'s class *and all its superclasses* for a data-descriptor. > > What is the benefit of Python's priority order? It's presumably not > just for the above situation, because having an instance variable and > a property of the same name is very much a corner case (note the need > to use `self.__dict__['a'] = 1` to create the instance attribute, > because the usual `self.a = 1` would invoke the property). > > Is there a different situation in which the "naive" lookup order would > cause a problem? >
It would create a disparity between lookup and assignment. When you assign to an attribute that has a data descriptor, it always invokes the descriptor. If it preferred to assign to the instance dict instead, the descriptor would never be invited. Now, suppose you have an object with a data descriptor, and an instance attribute of 42. We read the attribute and get 42 back, which is not what we want, so we assign 111 to it, then we read it back again, and the value is still 42! What gives? We're not able to overwrite or delete the value that were reading, and it's because an unexpected value crept into the instance dict somehow. The storage of a data descriptor value is expected to be defined by the descriptor, not by using the same key in the instance dict (although this scheme also means that the data descriptor could use that dict entry for its own storage if it wants). The reason you can do this for a non-data descriptor is because with no set or delete, they don't define their own storage, and you can thus set the instance attribute if you want to override the descriptor. > -- https://mail.python.org/mailman/listinfo/python-list