描述符是实现了特定协议的类,这个协议包括__get____set____delete__方法,property类实现了完整的描述符协议。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
class property(object):
    """
    property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
    
    fget is a function to be used for getting an attribute value, and likewise
    fset is a function for setting, and fdel a function for del'ing, an
    attribute.  Typical use is to define a managed attribute x:
    
    class C(object):
        def getx(self): return self._x
        def setx(self, value): self._x = value
        def delx(self): del self._x
        x = property(getx, setx, delx, "I'm the 'x' property.")
    
    Decorators make defining new properties or modifying existing ones easy:
    
    class C(object):
        @property
        def x(self):
            "I am the 'x' property."
            return self._x
        @x.setter
        def x(self, value):
            self._x = value
        @x.deleter
        def x(self):
            del self._x
    """
    def deleter(self, *args, **kwargs): # real signature unknown
        """ Descriptor to change the deleter on a property. """
        pass

    def getter(self, *args, **kwargs): # real signature unknown
        """ Descriptor to change the getter on a property. """
        pass

    def setter(self, *args, **kwargs): # real signature unknown
        """ Descriptor to change the setter on a property. """
        pass

    def __delete__(self, *args, **kwargs): # real signature unknown
        """ Delete an attribute of instance. """
        pass

    def __getattribute__(self, *args, **kwargs): # real signature unknown
        """ Return getattr(self, name). """
        pass

    def __get__(self, *args, **kwargs): # real signature unknown
        """ Return an attribute of instance, which is of type owner. """
        pass

    def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
        """"""
        pass

    @staticmethod # known case of __new__
    def __new__(*args, **kwargs): # real signature unknown
        """ Create and return a new object.  See help(type) for accurate signature. """
        pass

    def __set__(self, *args, **kwargs): # real signature unknown
        """ Set an attribute of instance to value. """
        pass

    fdel = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

    fget = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

    fset = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

    __isabstractmethod__ = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

描述符示例

  • 描述符类:实现描述符协议的类。
  • 托管类:把描述符实例声明成类属性的类。
  • 描述符实例:描述符类的各个实例,声明为托管类的类属性。
  • 托管实例:托管类的实例
  • 存储属性:托管实例中存储自身托管属性的属性。
  • 托管属性:托管类中由描述符实例处理的公开属性,值存储在存储属性中。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Quantity:  # 描述符类
    def __init__(self, storage_name):
        self.storage_name = storage_name

    def __set__(self, instance, value):  # 描述符类设置属性的值,instance是托管实例
        """
        设置描述符的属性的值
        :param instance: 托管类的实例
        :param value: 需要设置的值
        :return:
        """
        print("Attritube '{}' use Quantity class.__set__ set value.".format(self.storage_name))
        if value > 0:
            instance.__dict__[self.storage_name] = value  # 在托管实例的字典表中添加对应的属性值
        else:
            raise ValueError("value must be > 0.")


class LineItem:  # 托管类
    weight = Quantity("weight")  # 描述符实例
    price = Quantity("price")  # 描述符实例

    def __init__(self, description, weight, price):
        self.descript = description  # 存储属性
        self.weight = weight  # 托管属性
        self.price = price  # 托管属性

    def Subtotal(self):
        return self.weight * self.price


raisins = LineItem('Golden raisins', 10, 6.0)
>>> Attritube 'weight' use Quantity class.__set__ set value.
>>> Attritube 'price' use Quantity class.__set__ set value.

在描述符中,实现了__set__方法的描述符称为覆盖型描述符,没有实现__set__方法的描述符称为非覆盖型描述符

当实例取属性时,优先触发__get__方法,如果没有实现__get__方法,则取实例自身的属性。

设置属性时,优先触发__set__方法,如果没有实现__set__方法,则设置实例自身的属性,del此属性对类的同名属性没有影响。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
def cls_name(obj_or_cls):
    cls = type(obj_or_cls)
    if cls is type:
        cls = obj_or_cls
    return cls.__name__.split(".")[-1]


def display(obj):
    cls = type(obj)
    if cls is type:
        return "<class {}>".format(obj.__name__)  # 如果是类,返回类的描述
    elif cls in [type(None), int]:
        return repr(obj)  # 如果为空或为整数值,则返回对应的描述
    else:
        return "<{} object>".format(cls_name(obj))  # 如果是实例,返回实例描述


def print_args(name, *args):
    """
    打印参数
    :param name: __get__或__set__方法
    :param args: 包括 self(描述符实例), instance(托管类实例), owner(托管类)
    :return:
    """
    pseudo_args = ",".join(display(obj) for obj in args)
    print(
        "-> {cls_name}.__{get_or_set}__({args})".format(cls_name=cls_name(args[0]), get_or_set=name, args=pseudo_args))


# 重要的类

class Overload:
    """覆盖型描述符"""

    def __get__(self, instance, owner):
        print_args("get", self, instance, owner)

    def __set__(self, instance, value):
        print_args("set", self, instance, value)


class OverloadNotGet:
    """没有__get__方法的覆盖型描述符"""

    def __set__(self, instance, value):
        print_args("set", self, instance, value)


class NonOverload:
    """非覆盖型描述符"""

    def __get__(self, instance, owner):
        print_args("get", self, instance, owner)


class Manager:
    override = Overload()
    overrideNonGet = OverloadNotGet()
    nonOverride = NonOverload()

    def test(self):
        print("-> Manager.test({})".format(display(self)))


obj = Manager()

# ---------------------覆盖型,含 __get__ 和 __set__----------------------
obj.override  
>>> -> Overload.__get__(<Overload object>,<Manager object>,<class Manager>)
Manager.override  
>>> -> Overload.__get__(<Overload object>,None,<class Manager>)

obj.__dict__    # 在对override属性赋值前,__dict__内容
>>> {}
obj.override = 7
>>> -> Overload.__set__(<Overload object>,<Manager object>,7)
obj.override  
>>> -> Overload.__get__(<Overload object>,<Manager object>,<class Manager>)
obj.__dict__   # 在对override属性赋值后,__dict__内容
>>> {}

obj.__dict__["override"] = 9  # 通过__dict__给override赋值,不触发__set__方法
obj.override    # 取实例属性时,实际上仍取的是类的属性,触发__get__方法
>>> -> Overload.__get__(<Overload object>,<Manager object>,<class Manager>)


# ---------------------无get覆盖型 __set__-----------------
obj.overrideNonGet      # 类未实现__get__方法,直接获取的描述符的实例
>>> -> <__main__.OverloadNotGet object at 0x7f0bdd21eba8>
Manager.overrideNonGet  
>>> -> <__main__.OverloadNotGet object at 0x7f0bdd21eba8>

obj.__dict__    # 在对overrideNonGet属性赋值前,__dict__内容
>>> {'override': 9}
obj.overrideNonGet = 7  
>>> -> OverloadNotGet.__set__(<OverloadNotGet object>,<Manager object>,7)
obj.overrideNonGet
>>> -> <__main__.OverloadNotGet object at 0x7f0bdd21eba8>
obj.__dict__   # 在对OverloadNotGet属性赋值后,__dict__内容
>>> {'override': 9}

obj.__dict__["overrideNonGet"] = 8
obj.overrideNonGet  # overrideNonGet描述符中没有__get__方法,所以不触发,直接返回属性的值
>>> 8

# ---------------------非覆盖型 __get__--------------------
obj.nonOverride  
>>> -> NonOverload.__get__(<NonOverload object>,<Manager object>,<class Manager>)
Manager.nonOverride 
>>> -> NonOverload.__get__(<NonOverload object>,None,<class Manager>)

obj.__dict__    # 在对nonOverride属性赋值前,__dict__内容
>>> {'override': 9, 'overrideNonGet': 8}
obj.nonOverride = 7  #
obj.nonOverride  # 没有实现__set__方法,取值时不触发__get__方法    
>>> 7
obj.__dict__    # 在对nonOverride属性赋值后,__dict__内容
>>> {'override': 9, 'overrideNonGet': 8, 'nonOverride': 7}

Manager.nonOverride  # 类的nonOverride属性仍返回 __get__方法的内容
>>> -> NonOverload.__get__(<NonOverload object>,None,<class Manager>)