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.
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 callignsuper(), instead of creating a dict entry yourself:And in the interactive interpreter:
So, the behavior you observed in your
__set__in the descriptor is the ordinary behavior.