Both of them are python from documents :
the first one says:
If an object defines both
__get__()
and__set__()
, it is considered a data descriptor. Descriptors that only define__get__()
are called non-data descriptors (they are typically used for methods but other uses are possible).
the second one says:
If the descriptor defines
__set__()
and/or__delete__()
, it is a data descriptor; if it defines neither, it is a non-data descriptor. Normally, data descriptors define both__get__()
and__set__()
, while non-data descriptors have just the__get__()
method.
The question is : is it enough to only define __set__
to make a data descriptor ?
And we I refer to the python source code, I found this :
#define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set != NULL)
Seems we can only define __set__
without __get__
.
And then I turn to write some examples to prove what I got :
class GetSet(object):
def __get__(self, instance, cls =None):
print('__get__')
def __set__(self, obj, val):
print('__set__')
class Get(object):
def __get__(self, instance, cls =None):
print('__get__')
class Set(object):
def __set__(self, obj, val):
print('__set__')
class UserClass(object):
a = Get()
b = Set()
c = GetSet()
u = UserClass()
u.__dict__['a'] = 'a'
u.__dict__['b'] = 'b'
u.__dict__['c'] = 'c'
print('start')
print(u.a)
print(u.b)
print(u.c)
The output makes me confusing again:
start
a
b
__get__
None
According to the python attribute lookup orders : the priority of data descriptor is higher than obj.__dict__
.
My example shows : Only the descriptor defines both __set__
and __get__
makes it a data descriptor !
Which one is the right answer ?
__set__
--- > data descriptor
or
__get__
and __set__
---> data descriptor ?
The second quote is correct. The second quote comes from the Python language reference (though you've provided the wrong link), and the language reference is considered more authoritative than how-to guides. Also, it matches the actual behavior; the PyDescr_IsData
macro you found is the actual routine used in object.__getattribute__
to determine what counts as a data descriptor, and either __set__
or __delete__
will cause tp_descr_set
to be non-null.
The language reference also explains why Set
doesn't override the instance dict for a.b
:
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. [...] Data descriptors with__set__()
and__get__()
defined always override a redefinition in an instance dictionary.
Defining either __set__
or __delete__
will set a type's tp_descr_set
slot and make instances of the type data descriptors. A data descriptor will always be invoked for attempts to set or delete the attribute it manages, even if there is an entry in the instance's dict with the same name, and even if it only has __set__
and you're trying to delete the attribute or vice versa. (If it doesn't have the needed method, it will raise an exception.) If a data descriptor also has __get__
, it will also intercept attempts to get the attribute; otherwise, Python will fall back on the normal attribute lookup behavior, as if it wasn't a descriptor at all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With