如何总结get,set调用的规则或者逻辑?

p.attr = value
这个表达式,python解释器将从整体上,将它理解成一个赋值,触发set;而不会这样做,p.attr触发get,get代码执行完后,p.attr = value再触发set.

class Sample:
    def __init__(self, name):
        self.name = name
    def __get__(self,instance,owner):
        print('get called')
    def __set__(self, instance, value):
        print('set called')


class Person:
    name = Sample('name')

我们测试一下

p = Person()
p.name
get called
p.name = 'tom'
set called

好,我们来点其他的代码,观察一下python解释器

import time
class Room:
    def __init__(self,name):
        self.name = name
    def __getattribute__(self,key):
        print('in __getattribute__',key)
        return  object.__getattribute__(self,key)
    def __setattr__(self,attr,value):
        print('in __setattr__',attr)
        time.sleep(3)
        self.__setattr__(attr, value)
        print('over')

初始化,看看发生什么

x = Room('r1')
in __setattr__ name
in __getattribute__ __setattr__
in __setattr__ name
in __getattribute__ __setattr__
in __setattr__ name
in __getattribute__ __setattr__
in __setattr__ name
in __getattribute__ __setattr__
in __setattr__ name
in __getattribute__ __setattr__
in __setattr__ name

我试图解释一下,添加一点注释

x = Room('r1')
in __setattr__ name                  #self.name = name 触发setattr
in __getattribute__ __setattr__      #python读取self.__setattr__,触发get
in __setattr__ name                  #get执行完成后,又进入了set
in __getattribute__ __setattr__
in __setattr__ name
in __getattribute__ __setattr__
in __setattr__ name
in __getattribute__ __setattr__
in __setattr__ name
in __getattribute__ __setattr__
in __setattr__ name

这句话
self.__setattr__(attr, value)
python解释器,读取了一部分self.__setattr__,触发get;get执行完成后,python解释器又从整体上self.__setattr__(attr, value),将它理解成set,进入set后,又碰上
self.__setattr__(attr, value)
于是不断循环,这个逻辑为何不作用到第一个例子?

python解释器读取p.name = 'tom'时,p.name触发get,然后整体上是一个set
为何不输出下面的结果:

p.name = 'tom'
get called
set called

请总结一下里面的规则。

阅读 2.1k
1 个回答

考虑到自己曾经写的的笔记里没有这部分内容,所以特意查了下网络上已有的知识,先做个简要的总结在这里。
小注:在属性的读取和赋值过程中,Python解释器不会分别调用get和set方法。它会根据上述规则选择调用适当的方法来处理属性的读取和赋值操作。

属性的读取:

当我们使用instance.attr语法获取属性值时,Python解释器将按照以下顺序进行操作:
首先,它会检查是否定义了一个名为__getattribute__()的方法。如果定义了,则调用该方法。
如果未定义__getattribute__()方法,它会检查是否定义了一个名为__getattr__()的方法。如果定义了,则调用该方法。
如果以上两个方法都未定义,它会直接从实例的字典中获取属性的值。

属性的赋值:

当我们使用instance.attr = value语法给属性赋值时,Python解释器将按照以下顺序进行操作:
首先,它会检查是否定义了一个名为__setattr__()的方法。如果定义了,则调用该方法。
如果未定义__setattr__()方法,它会直接将属性添加到实例的字典中。

所以回过头看,在你提供的代码示例中,第一个例子中的Sample类是一个描述符类,它实现了描述符协议的__get__()和__set__()方法。这是一个特殊情况,当我们将一个描述符对象赋值给类的属性时,Python解释器会自动调用描述符的__get__()和__set__()方法来处理属性的读取和赋值。

而第二个例子中的Room类并不是一个描述符类,它定义了__getattribute__()和__setattr__()方法来自定义属性的读取和赋值操作。但是,在__setattr__()方法中,你使用了递归调用self.__setattr__(attr, value),导致了无限循环。这是因为在赋值过程中,每次调用__setattr__()方法都会再次触发__setattr__()方法,形成了无限递归。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进