Reputation: 26900
I read somewhere about the fact you can have a descriptor with __set__
and without __get__
.
How does it work?
Does it count as a data descriptor? Is it a non-data descriptor?
Here is a code example:
class Desc:
def __init__(self, name):
self.name = name
def __set__(self, inst, value):
inst.__dict__[self.name] = value
print("set", self.name)
class Test:
attr = Desc("attr")
>>>myinst = Test()
>>> myinst.attr = 1234
set attr
>>> myinst.attr
1234
>>> myinst.attr = 5678
set attr
>>> myinst.attr
5678
Upvotes: 5
Views: 666
Reputation: 26900
The descriptor you've given in the example is a data descriptor.
Upon setting the attribute, as any other data descriptor, it takes the highest priority and is called like so:
type(myinst).__dict__["attr"].__set__(myinst, 1234)
This in turn, adds attr
to the instance dictionary according to your __set__
method.
Upon attribute access, the descriptor is checked for having the __get__
method but fails, causing for the search to be redirected to the instance's dictionary like so:
myinst.__dict__["attr"]
If it is not found in the instance dictionary, the descriptor itself is returned.
This behavior is shortly documented in the data model like so:
If it does not define
__get__()
, then accessing the attribute will return the descriptor object itself unless there is a value in the object’s instance dictionary.
Common usecases include avoiding {instance: value}
dictionaries inside the descriptors, and caching values in an efficient way.
In Python 3.6, __set_name__
was added to the descriptor protocol thus eliminating the need for specifying the name inside the descriptor. This way, your descriptor can be written like so:
class Desc:
def __set_name__(self, owner, name):
self.name = name
def __set__(self, inst, value):
inst.__dict__[self.name] = value
print("set", self.name)
class Test:
attr = Desc()
Upvotes: 7