证明了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
型
所以我觉得有些规律或者逻辑还是没有总结出来。
1条答案
按热度按时间bfrts1fy1#
Assignemts将触发普通对象的
__setattr__
,并在描述符中调用__set__
方法,正如您验证的那样。您的第一个示例运行
__getattribute__
来检索示例__dict__
的唯一原因是 * 您的 * 自定义__setattr__
实现这样做了。默认的
__setattr__
通常会在示例__dict__
中创建一个条目,但该条目将在本地代码中使用内部插槽引用,而不是通过__gettattribute__
重定向。您可以通过调用
super()
的默认__setattr__
实现来验证这一点,而不是自己创建一个dict条目:字符串
在交互式解释器中:
型
因此,您在描述符中的
__set__
中观察到的行为是普通行为。