2

我正在阅读O Reilley Python Cookbook,我对以下代码有疑问:

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]

如果未设置实例(即无),那么它说返回“自我”,因为自我表示类描述符,究竟返回什么?

4

2 回答 2

1

然后它说返回'self',因为self表示类描述符究竟返回什么?

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

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

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
于 2020-01-10T22:32:00.080 回答
1

是的,它返回描述符实例。

第二个参数(第一个 after self__get__要么是在其上查找描述符的实例,要么是None在类上查找它。

因此,在给定的情况下,它会返回描述符,以防您在类上查找描述符。

只是为了说明:

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__(可能在所有超类中)。在大多数情况下,在类上查找属性时做任何事情都是没有意义的,因此返回实例是一个好主意。

这也是内置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)>

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

于 2020-01-10T23:00:15.833 回答