Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

which one is the right definition of data-descriptor and non-data descriptor?

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 ?

like image 373
oyjh Avatar asked May 19 '15 01:05

oyjh


1 Answers

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.

like image 186
user2357112 supports Monica Avatar answered Oct 31 '22 11:10

user2357112 supports Monica