python解释器如何决定何时调用”set”和”get”方法,何时不调用?

How does python interpreter decides when to invokes the __set__ and __get__ methods and when not to?

本问题已经有最佳答案,请猛点这里访问。

假设我们定义下面的python描述符

1
2
3
4
5
6
7
8
9
10
11
12
class FooDescriptor():
    def __init__(self, name='foo', initval='initval'):
        self.name = name
        self.val = initval

    def __get__(self, instance, cls):
        print("invoke get {} in instance {} in class {}".format(self.name, instance, cls))
        return self.val

    def __set__(self, instance, val):
        print("invoke set {} in instance {}".format(self.name, instance))
        self.val = val

现在,如果我们将类Foo定义为具有类型FooDescriptor的类属性和相同类型的实例属性:

1
2
3
4
class Foo():
    cdescr = FooDescriptor(name='class attribute descr', initval=1)
    def __init__(self):
        self.idescr = FooDescriptor(name='instance attribute descr', initval=2)

我们得到类描述符所需的行为:

1
2
3
4
5
6
7
8
9
>>> foo = Foo()
>>> foo.cdescr
invoke get class attribute descr in instance <__main__.Foo object at 0x10189ecc0> in class <class '__main__.Foo'>
1
>>> foo.cdescr = 100
invoke set class attribute descr in instance <__main__.Foo object at 0x10189ecc0>
>>> foo.cdescr
invoke get class attribute descr in instance <__main__.Foo object at 0x10189ecc0> in class <class '__main__.Foo'>
100

但是,对于实例属性idescr,当访问或设置属性时,描述符对象的__set____get__方法不会被调用:

1
2
3
4
5
6
7
>>> foo.idescr
<__main__.FooDescriptor object at 0x10189ecf8>
>>> foo.idescr = 120
>>> foo.idescr
120
>>> type(foo.idescr)
<class 'int'>

如果能更好地理解描述符的范围以及当属性protocal .出现时,python如何决定调用它们的__set____get__,我们将不胜感激。


这是因为在查找实例类的属性时使用描述符。如果它是一个实例属性,它就不会影响描述符协议。

这类问题的一个很好的来源是IONEL关于元类的博客,其中包含了这张图片,在使用描述符时也会显示:

enter image description here

(从此处复制的图像)

因为cdescrclass.__dict__中,并且有__get____set__两个参数,它将返回描述符__get__返回的值。然而,由于idescr不在class.__dict__中,它只返回存储在instance.__dict__中的内容。