-1

我目前正在阅读O Reilly Python Cookbook,但我对以下代码感到困惑:

class Descriptor:
    def __init__(self, name=None, **opts):
        self.name = name
        self.__dict__.update(opts)

    def __set__(self, instance, value):
        instance.__dict__[self.name] = value

# Descriptor for enforcing types
class Typed(Descriptor):
    expected_type = type(None)

    def __set__(self, instance, value):
        if not isinstance(value, self.expected_type):
            raise TypeError('expected ' + str(self.expected_type))
        super().__set__(instance, value)

class Integer(Typed):
    expected_type = int

我知道如何设置实例属性。

我对以下行感到困惑:

instance.__dict__[self.name] = value

为什么要这样做?因为这意味着可以为实例属性指定一个与声明类时最初设置的名称不同的名称。例如:

class A:
    x = Integer('d')

b=A()

b.x=5
b.x
<__main__.Integer at 0x1188d1390>

b.d
5

我们已经用 b 覆盖了名称 x。为什么允许这样做?

我也对init函数中的以下内容感到困惑:

self.__dict__.update(opts)

为什么我们要使用附加属性填充类型检查描述符?

这些添加的属性有什么作用?

4

1 回答 1

2

该说明书最后一次更新是在 2013 年,比 Python 3.6 发布早三年,它__set_name__为描述符协议添加了一个方法。这个方法是在创建描述符的时候自动调用的,所以写

x = Integer()

会让描述符看到它被分配了一个名称x并直接设置它自己的名称属性。

class Descriptor:
    def __init__(self, **opts):
        self.__dict__.update(opts)

    # I don't *think* we need to know which class
    # the descriptor is being added to for this use case,
    # but I might be overlooking something
    def __set_name__(self, owner, name):
        self.name = name

    def __set__(self, instance, value):
        instance.__dict__[self.name] = value

附加属性可以让您定义“受限”类型。例如,您可以将值限制在 1 到 10 之间:

class RestrictedInteger(Integer):
    def __set__(self, instance, value):
        if not (self.low <= value <= self.high):
            raise ValueError(f"{value} not in range {self.low}-{self.high}")
        super().__set__(instance, value)

class A:
    x = RestrictedInteger(low=1, high=10)

然后

>>> b = A()
>>> b.x = 5
>>> b.x
5
>>> b.x = 11
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "tmp.py", line 29, in __set__
    raise ValueError(f"{value} not in range {self.low}-{self.high}")
ValueError: 11 not in range 1-10
>>> b.x = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "tmp.py", line 29, in __set__
    raise ValueError(f"{value} not in range {self.low}-{self.high}")
ValueError: 0 not in range 1-10
于 2020-01-15T22:17:58.410 回答