I am going to borrow Marty Alchin's answer:
... the fact that only
__get__
can receive theowner
class, while the rest only receive the instance.
Descriptors are assigned to a class, not to an instance, and modifying the class would actually overwrite or delete the descriptor itself, rather than triggering its code. This is intentional. Otherwise, once a descriptor is set, it could never be removed or modified without modifying source code and restarting the program. That’s not preferable, so only the retrieval method (
__get__
) has access to the owner class.
An example to show what is explained above. The code also explains this portion of @Gery Ogam's answer:
import random
class Die:
def __init__(self, sides=6):
self.sides = sides
def __get__(self, instance, owner=None):
return int(random.random() * self.sides) + 1
def __set__(self, instance, value):
...
class Game:
d6 = Die()
# Assume:
# if `__set__` was designed to take an `owner` class,
# the code below will trigger the `__set__` method
# rather than call `__get__` to override
# the value of `d6` in the `Game.__dict__`.
Game.d6 = 3
print(Game.d6) # 3
The
owner
will always be set to the appropriate class, thoughinstance
may beNone
if the attribute was accessed from the class. This is what Django uses to throw an error if you try to access a manager from an object instead of a class, or a related model (ForeignKey) on a class instead of an object.