python-3.x 为什么self.__dict__可以触发getattribute而instance.attribute不能?

nbysray5  于 2023-08-08  发布在  Python
关注(0)|答案(1)|浏览(109)

证明了self.__dict__可以触发描述符中的__getattribute__

class Room:
    def __init__(self,name):
        self.name = name
    def __getattribute__(self,attr):
        print('in __getattribute__',attr)
        return  object.__getattribute__(self,attr)
    def __setattr__(self,attr,value):
        print('in __setattr__',attr)
        self.__dict__[attr] = value
        print('over')

字符串
创建一个示例,我在输出信息中添加一些以#开头的注解:

x = Room('r1')
# self.name = name in __init__ trigger __setattr__
in __setattr__ name  
# python interpreter read part of self.__dict__[attr] = value,extract self.__dict__ to trigger __getattribute__.
in __getattribute__ __dict__  
over


我们看到python解释器可以提取整行中的部分字符串来触发__getattribute__方法。

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可以触发__get__

p.name = 'tom'
set called


对于p.name = 'tom'命令,为什么python解释器不提取p.name调用__get__,然后让p.name = 'tom'调用__set__?为什么输出不像下面这样?

p.name = 'tom'
get called   # p.name  trigger
set called   # p.name = 'tom' trigger


@jsbueno,默认的__setattr__通常会在示例__dict__中创建一个条目,但该条目将在本机代码中引用,使用内部插槽,而不是通过__gettattribute__重定向。这不是真的。

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


如果我们写self.__setattr__(attr, value)而不是super.__setattr__(attr, value),python解释器提取self.__setattr__并重定向到__gettattribute__,然后将super.__setattr__(attr, value)整体解析为__setattr__,再次跳转到__setattr__,无休止地重复,直到你用ctl+c停止它。

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


从以上代码中我们得出了一些关于数据描述符的结论。
1.不触发__getattribute__的表达式。

super().__setattr__(attr, value)  
object.__setattr__(self,attr, value)  # i have checked


2.触发__getattribute__的表达式。

self.__setattr__(attr, value)
self.__dict__[attr] = value


self.__setattr__触发__getattribute__时,__setattr__被解析为attr。
self.__dict__触发__getattribute__时,__dict__被解析为attr。
谜题仍在这里。
谜题1:结构相同:

super().__setattr__(attr, value)  
object.__setattr__(self,attr, value)
self.__setattr__(attr, value)


,python解释器解析前两个表达式为属性的设置值,从不触发get也不触发set,为什么python解释器读取self.__setattr__(attr, value)中的部分代码片段self.__setattr__,要匆忙触发get__getattribute__)?
谜题二:
self.__dict__[attr] = value整体上是一个赋值,python解释器提取部分代码片段self.__dict__,来触发get,为什么python不提取__init__self.name = name中的self.name来触发get__getattribute__),同样的动作?
谜题三:
在属性管理上深入挖掘,更多奇怪的动作让我震惊。由于上面的代码显示self.__setattr__(attr, value)在数据描述符中触发__getattribute__,因此在下面的代码中它触发set而不是get

import time
class Room:
     def __init__(self,value):
         self.name = value  
     def get_name(self):
         print('in get')
         return self.__dict__['name']       
     def set_name(self, value):
         print('in set')
         time.sleep(3)
         self.__setattr__('name', value)
     name = property(get_name, set_name) 
r = Room('r1')
in set
in set
in set
in set


所以我觉得有些规律或者逻辑还是没有总结出来。

bfrts1fy

bfrts1fy1#

Assignemts将触发普通对象的__setattr__,并在描述符中调用__set__方法,正如您验证的那样。
您的第一个示例运行__getattribute__来检索示例__dict__的唯一原因是 * 您的 * 自定义__setattr__实现这样做了。
默认的__setattr__通常会在示例__dict__中创建一个条目,但该条目将在本地代码中使用内部插槽引用,而不是通过__gettattribute__重定向。
您可以通过调用super()的默认__setattr__实现来验证这一点,而不是自己创建一个dict条目:

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)
        super().__setattr__(attr, value)
        print('over')

字符串
在交互式解释器中:

In [35]: r = Room("blah")
in __setattr__ name
over
In [36]: r.name
in __getattribute__ name
Out[36]: 'blah'

In [37]: r.__dict__
in __getattribute__ __dict__
Out[37]: {'name': 'blah'}


因此,您在描述符中的__set__中观察到的行为是普通行为。

相关问题