为什么这个 class 描述符 __get__ 方法 return 自我?

Why does this class descriptor __get__ method return self?

我正在研究 O Reilley Python 食谱,我对以下代码有疑问:

class Typed:
    def __init__(self, name, expected_type):
        self.name = name
        self.expected_type = expected_type

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if not isinstance(value, self.expected_type):
            raise TypeError('Expected ' + str(self.expected_type))
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        del instance.__dict__[self.name]

# Class decorator that applies it to selected attributes
def typeassert(**kwargs):
    def decorate(cls):
        for name, expected_type in kwargs.items():
            # Attach a Typed descriptor to the class
            setattr(cls, name, Typed(name, expected_type))
        return cls
    return decorate

# Example use
@typeassert(name=str, shares=int, price=float)
class Stock:
    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price

if __name__ == '__main__':
    s = Stock('ACME', 100, 490.1)
    print(s.name, s.shares, s.price)
    s.shares = 50
    try:
        s.shares = 'a lot'
    except TypeError as e:
        print(e)

我对这部分感到困惑:


def __get__(self, instance, cls):
    if instance is None:
        return self
    else:
        return instance.__dict__[self.name]

如果未设置实例(即 None),那么它会显示 return 'self',因为 self 代表 class 描述符 returned?

then it says return 'self', given that self represents the class descriptor what exactly is returned?

您刚刚回答了您自己的问题:它 returns 描述符对象本身。

因此,当在 class 上访问描述符时,instance 将是 None,在这种情况下它只是 returns 描述符。考虑 Descriptor HOWTO 中 Python 中重新实现的 property 示例:

In [5]: class Property(object):
   ...:     "Emulate PyProperty_Type() in Objects/descrobject.c"
   ...:
   ...:     def __init__(self, fget=None, fset=None, fdel=None, doc=None):
   ...:         self.fget = fget
   ...:         self.fset = fset
   ...:         self.fdel = fdel
   ...:         if doc is None and fget is not None:
   ...:             doc = fget.__doc__
   ...:         self.__doc__ = doc
   ...:
   ...:     def __get__(self, obj, objtype=None):
   ...:         if obj is None:
   ...:             print('obj is None in Property.__get__')
   ...:             return self
   ...:         if self.fget is None:
   ...:             raise AttributeError("unreadable attribute")
   ...:         return self.fget(obj)
   ...:
   ...:     def __set__(self, obj, value):
   ...:         if self.fset is None:
   ...:             raise AttributeError("can't set attribute")
   ...:         self.fset(obj, value)
   ...:
   ...:     def __delete__(self, obj):
   ...:         if self.fdel is None:
   ...:             raise AttributeError("can't delete attribute")
   ...:         self.fdel(obj)
   ...:
   ...:     def getter(self, fget):
   ...:         return type(self)(fget, self.fset, self.fdel, self.__doc__)
   ...:
   ...:     def setter(self, fset):
   ...:         return type(self)(self.fget, fset, self.fdel, self.__doc__)
   ...:
   ...:     def deleter(self, fdel):
   ...:         return type(self)(self.fget, self.fset, fdel, self.__doc__)
   ...:

然后:

In [6]: class Foo:
   ...:     @Property
   ...:     def bar(self):
   ...:         return 42
   ...:

In [7]: Foo.bar
obj is None in Property.__get__
Out[7]: <__main__.Property at 0x1044d02b0>


In [8]: Foo().bar
Out[8]: 42

是的,它 returns 描述符实例。

__get__ 的第二个参数(self 之后的第一个参数)是查找描述符的实例 - 或者 None 如果它是在 class.

因此,在给定的情况下,它是 returns 描述符,以防您在 class 上查找描述符。

只是为了说明:

class Descriptor:
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return 10

class Test:
    test = Descriptor()


>>> Test.test
<__main__.Descriptor at 0x2769b7d44c8>

>>> Test.__dict__['test']
<__main__.Descriptor at 0x2769b7d44c8>

现在,如果它不使用 return self,它将看起来像这样:

class Descriptor:
    def __get__(self, instance, owner):
        return 10

class Test:
    test = Descriptor()


>>> Test.test
10

>>> Test.__dict__['test']
<__main__.Descriptor at 0x2769b7de208>

这个 return self 经常这样做的原因是因为它允许获取描述符实例而不必在 __dict__ 中搜索(可能在所有 superclasses 中)。在大多数情况下,当在 class 上查找 属性 时,做任何事情都是没有意义的,因此返回实例是个好主意。

这也是内置 property 所做的(以及函数描述符):

class A:
    @property
    def testproperty(self):
        return 10

    def testmethod(self):
        return 10

>>> A.testproperty
<property at 0x2769b7db9a8>
>>> A.testproperty.__get__(None, A)
<property at 0x2769b7db9a8>

>>> A.testmethod
<function __main__.A.testmethod(self)>
>>> A.testmethod.__get__(None, A)
<function __main__.A.testmethod(self)>

在 class(例如内置 staticmethodclassmethod 描述符)上查找属性时应该发生一些有意义的事情的情况下,那当然是different and self 不应该返回那里!