Python -- manage attributes (2)
_ Getattr _ and _ getattribute __
We have already introduced feature property and descriptor to manage specific properties, the _ getattr _ and _ getattribute _ operator overload methods provide another way to intercept attributes of class instances. They are more widely used, however, their performance is different:
[1] _ getattr _ targetUndefined attributesRun -- that is, attributes are not stored on the instance, or are not inherited from one of its classes.
[2] _ getattribute _ targetEach attributeTherefore, when using it, you must be careful to avoid recursive loops caused by passing attribute access to the superclass.
These two methods are representative of a set of property interception methods. These methods also include _ setattr _ and _ delattr __, which have the same effect.
Unlike features and descriptors, these methods are part of the Python [operator overload] Protocol-a special class naming method inherited by sub-classes. The _ getattr _ and _ getattribute _ methods are also common in BIT and descriptor. They are used to block the acquisition of almost all instance attributes, not just specific names. Therefore, these two methods are suitable for common delegate-based encoding methods.
Certificate ----------------------------------------------------------------------------------------------------------------------------------------
Basic knowledge
If a class defines or inherits the following method, an instance runs automatically when it is used in the case mentioned in the subsequent Annotations:
Def _ getattr _ (self, name): # reference an instance's undefined property obj. namedef _ getattribute (self, name): # reference all attributes obj. namedef _ setattr _ (self, name, value): # set any attribute obj. name = valuedef _ delattr _ (self, name, value): # delete any attribute del obj. name
In all these cases, self is usually the object of the main instance. name is the string name of the attribute to be accessed, and value is the object to be assigned to this attribute. Two get methods usually return the value of one attribute, and the other two methods return None. For example, to capture the acquisition of each attribute, we can use the two methods above. to capture the attribute assignment, we can use the third method:
>>> class Catcher:def __getattr__(self,name):print('Get:',name)def __setattr__(self,name,value):print('Set:',name,value)>>> X = Catcher()>>> X.jobGet: job>>> X.payGet: pay>>> X.pay = 99Set: pay 99
Certificate ----------------------------------------------------------------------------------------------------------------------------------------
Avoid loops in the property blocking method
Use these methods to avoid potential recursive loops.
For example, if another attribute is obtained in the code of _ getattribute _, _ getattribute __is triggered again, and the code will cyclically know that the memory is exhausted:
def __getattribute__(self,name):x = self.other
To solve this problem, point the get to a higher superclass, instead of skipping the version at this level. The object class is always a superclass, and it works well here:
def __getattribute__(self,name):x = object.__getattribute__(self,'other')
For _ setattr __, the situation is similar. assigning any attribute in this method triggers _ setattr _ again and creates a similar loop:
def __setattr__(self,name,value):self.other = value
To solve this problem, the attribute is assigned as a key value in the namespace Dictionary of the _ dict _ instance, thus avoiding direct attribute assignment:
def __setattr__(self,name,value):self.__dict__['other'] = value
Another method that is not commonly used is __setattr _. You can also assign values to your own attributes to a higher superclass to avoid loops, just like _ getattribute:
def __setattr__(self,name,value):object.__setattr__(self,'other',value)
Instead, we cannot use the _ dict _ technique to avoid loops in _ getattribute:
def __getattribute__(self,name):x = self.__dict__['other']
The obtained _ dict _ attribute triggers _ getattribute __again, resulting in recursive loops.
Certificate ----------------------------------------------------------------------------------------------------------------------------------------
Example
Here is an example of the same features and descriptors, but it is implemented using the attribute operator overload method.
Class Person: def _ init _ (self, name): self. _ name = name def _ getattr _ (self, attr): if attr = 'name': print ('fetch... ') return self. _ name else: raise AttributeError (attr) def _ setattr _ (self, attr, value): if attr = 'name': print ('change... ') attr =' _ name' self. _ dict _ [attr] = value def _ delattr _ (self, attr): if attr = 'name': print ('remove... ') attr =' _ name' del self. _ dict _ [attr] bob = Person ('Bob Smith ') print (Bob. name) bob. name = 'Robert Smith 'print (bob. name) del bob. nameprint ('-' * 20) sue = Person ('sue Jones ') print (Sue. name) # print (Person. name. _ doc _) # There is no usage equivalent to the feature here
Note that _ setattr __is also triggered by attribute assignment in the _ init _ constructor. This method captures each attribute assignment, even those in the class itself. Running this code produces the same output:
fetch...Bob Smithchange...fetch...Robert Smithremove...--------------------fetch...Sue Jones
Note that, unlike features and descriptors, the specified document is not directly declared for attributes.
To implement the same result as _ getattribute _, replace _ getattr __with the following code. because it captures all the attributes, therefore, you must pass the new Get To The superclass to avoid loops:
def __getattribute__(self,attr):if attr == 'name':print('fetch...')attr = '_name'return object.__getattribute__(self,attr)
Although these examples are consistent with the features and descriptor code, they do not emphasize the usefulness of these tools. Because they are common, _ getattr _ and _ getattribute _ are more common in delegate-based code. However, it is better to use features and Descriptors when only one attribute needs to be used normally.