英文:
Why self.__dict__ can trigger getattribute and instance.attribute can't?
问题
代码示例表明,在数据描述符中,有一些不同的表现和行为,这可能是因为Python解释器的内部实现细节。以下是对您提出的谜题的一些可能的解释:
Puzzle1:
super().__setattr__(attr, value)
和 object.__setattr__(self, attr, value)
这两个表达式被解释为设置属性值的操作,因此它们不会触发__get__
或__getattribute__
。这是因为它们被视为直接的属性赋值,而不是通过__getattribute__
机制访问属性。这是Python解释器的内部行为,不会触发get
或set
方法。
Puzzle2:
在 self.__dict__[attr] = value
中,self.__dict__
是一个字典,Python解释器解析这一部分时会触发__getattribute__
,因为它需要访问__dict__
属性。然而,在 self.name = name
中,self.name
被视为直接的属性分配,不会触发__getattribute__
。这是因为在Python中,属性分配和属性访问是不同的操作,它们在解释器中的处理方式也不同。
Puzzle3:
在 self.__setattr__('name', value)
中,您正在调用set_name
方法,这个方法在内部执行了self.__setattr__('name', value)
,这导致了递归的调用。这是为什么您看到多次 "in set" 输出的原因。在这个情况下,set_name
方法内部的self.__setattr__('name', value)
不会触发__getattribute__
,因为它只是调用了属性的设置操作,而不是属性的获取操作。
总之,Python的属性访问和赋值操作在内部有不同的实现机制,这些机制可能会导致不同的行为。这些谜题的解释可能涉及Python解释器的内部细节和优化策略。如果需要深入了解这些细节,可能需要查阅Python解释器的源代码或更深入的文档。
英文:
The proof verify that self.__dict__
can trigger __getattribute__
in descriptor.
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')
Create an instance,i add some comments starting with #
in the output info:
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
We saw that python interpreter can extract part of string in a whole line to trigger __getattribute__
method.
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')
Make an instance:
p = Person()
p.name
get called
p.name
can trigger __get__
p.name = 'tom'
set called
For the command p.name = 'tom'
,why python interpreter do not extract p.name
invoke __get__
and then make p.name = 'tom'
invoke __set__
? Why the output is not as below?
p.name = 'tom'
get called # p.name trigger
set called # p.name = 'tom' trigger
@jsbueno,The default __setattr__
will, ordinarily, create an entry in the instance __dict__
,
but that one will be refereed to in native code, using the internal slots, and not be redirected through __gettattribute__
.It is not true.
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')
If we write self.__setattr__(attr, value)
instead of super.__setattr__(attr, value)
,python interpreter extract self.__setattr__
and redirect into __gettattribute__
,then
parse super.__setattr__(attr, value)
in the whole as __setattr__
,jump into __setattr__
again,repeat without endness until you stop it by 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
We draw some conclusion on data descriptor from above codes.
1.expressions without triggering __getattribute__
.
super().__setattr__(attr, value)
object.__setattr__(self,attr, value) # i have checked
2.expressions with triggering __getattribute__
.
self.__setattr__(attr, value)
self.__dict__[attr] = value
When self.__setattr__
trigger __getattribute__
,__setattr__
was parsed as attr.
When self.__dict__
trigger __getattribute__
,__dict__
was parsed as attr.
Puzzles remain here.
Puzzle1:
With same structure:
super().__setattr__(attr, value)
object.__setattr__(self,attr, value)
self.__setattr__(attr, value)
,python interpreter parse the first two expressions as setting value for attribute,never triger get
nor set
,Why python interpreter read part of code snippet self.__setattr__
in self.__setattr__(attr, value)
,to trigger get
(__getattribute__
) in a hurry?
Puzzle2:
self.__dict__[attr] = value
is an assignment in the whole,python interpreter extract part code snippet self.__dict__
,to trigger get
,why don't python extract self.name
in self.name = name
in __init__
to trigger get
(__getattribute__
) ,with same action ?
Puzzle3:
Dig more deeper on property management,more strange action shocked me.As the above codes show that self.__setattr__(attr, value)
trigger __getattribute__
in data descriptor,it trigger set
instead of get
in below code:
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
So i feel that some rules or logic still do not be summarized.
答案1
得分: 2
Assignemts will trigger __setattr__
for ordinary objects, and call the __set__
method in descriptors, as you verified.
The only reason your first example runs __getattribute__
to retrieve the instance __dict__
is because your custom implementation of __setattr__
does so.
The default __setattr__
will, ordinarily, create an entry in the instance __dict__
, but that one will be refereed to in native code, using the internal slots, and not be redirected through __gettattribute__
.
You can verify this easily by using the default __setattr__
implementation by callign super()
, instead of creating a dict entry yourself:
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')
And in the interactive interpreter:
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'}
So, the behavior you observed in your __set__
in the descriptor is the ordinary behavior.
英文:
Assignemts will trigger __setattr__
for ordinary objects, and call the __set__
method in descriptors, as you verified.
The only reason your first example runs __getattribute__
to retrieve the instance __dict__
is because your custom implementation of __setattr__
does so.
The default __setattr__
will, ordinarily, create an entry in the instance __dict__
, but that one will be refereed to in native code, using the internal slots, and not be redirected through __gettattribute__
.
You can verify this easily by using the default __setattr__
implementation by callign super()
, instead of creating a dict entry yourself:
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')
And in the interactive interpreter:
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'}
So, the behavior you observed in your __set__
in the descriptor is the ordinary behavior.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论