object descript...LOGIN

object descriptor

Generally speaking, a descriptor is an object attribute with "binding behavior", and its access control is overridden by the descriptor protocol method. These methods are __get__(), __set__(), and __delete__(). Objects with these methods are called descriptors.

The default access control for attributes is to get (get), set (set) and delete (delete) from the object's dictionary (__dict__). For example, the search order for a.x is, a.__dict__['x'], then type(a).__dict__['x'], and then find the parent class of type(a) (excluding metaclass) .If the value found is a descriptor, Python will call the descriptor's methods to override the default control behavior. Where in the lookup phase this overriding occurs depends on which descriptor method is defined. Note that descriptors only work when inside a new-style class. New-style classes and old-style classes have been mentioned in the previous chapters. If you are interested, you can check out the previous chapters. The biggest feature of new-style classes is that all classes inherit from type or object classes.

In object-oriented programming, if the attributes of a class are interdependent, using descriptors to write code can organize the logic very cleverly. In Django's ORM, fields such as InterField in models.Model implement their functions through descriptors.

Let’s take a look at the following example first:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
class User(object):
    def __init__(self, name='两点水', sex='男'):
        self.sex = sex
        self.name = name
    def __get__(self, obj, objtype):
        print('获取 name 值')
        return self.name
    def __set__(self, obj, val):
        print('设置 name 值')
        self.name = val
class MyClass(object):
    x = User('两点水', '男')
    y = 5
if __name__ == '__main__':
    m = MyClass()
    print(m.x)
    print('\n')
    m.x = '三点水'
    print(m.x)
    print('\n')
    print(m.x)
    print('\n')
    print(m.y)

The output results are as follows:

获取 name 值
两点水
设置 name 值
获取 name 值
三点水
获取 name 值
三点水
5

Through this example, we can well observe the __get__() and _ _set__() calls to these methods.

Look at another classic example

We know that distance can be expressed in units of "meters" or "feet". Now we define a class to represent distance, which has two properties: meters and feet.

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
class Meter(object):
    def __init__(self, value=0.0):
        self.value = float(value)
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        self.value = float(value)
class Foot(object):
    def __get__(self, instance, owner):
        return instance.meter * 3.2808
    def __set__(self, instance, value):
        instance.meter = float(value) / 3.2808
class Distance(object):
    meter = Meter()
    foot = Foot()
if __name__ == '__main__':
    d = Distance()
    print(d.meter, d.foot)
    d.meter = 1
    print(d.meter, d.foot)
    d.meter = 2
    print(d.meter, d.foot)

Output results:

0.0 0.0
1.0 3.2808
2.0 6.5616

In the above example, before assigning a value to the instance of Distance, we think that meter and foot should be instance objects of their respective classes, but the output is numerical value. This is because __get__ plays a role.

We just modified meter and assigned it to int, but foot was also modified. This is where __set__ comes into play.

The descriptor object (Meter, Foot) cannot exist independently, it needs to be held by another owner class (Distance). The descriptor object can access the properties of its owner instance, such as instance.meter of Foot in the example.

Next Section
submitReset Code
ChapterCourseware