To learn and use good Python, descriptor is a point that must span the past, and now, although Python books are a lot of tricks, but it seems to be introduced in some Python libraries, the Python language itself is very little attention, or even attention, but can introduce the Dscriptor introduced clearly, is very few, to the present, I have not seen.
A attr can be referred to as descriptor, except for the need to define descriptor protocol prescribed methods, this attr must belong to a class, not belong to a certain instance
First, the descriptor in Python
Overriding any of the following methods in a Python class is called descriptor
1.__get__ (self,obj,type=none)---->value
2.__set__ (self,obj,value)---->none
3.__DELETE__ (self,obj)---->none
Descriptor Subdivision:
1.Data descriptor: Just rewrite __get__,__set__ 's class
2.None Data Descriptor: Just rewrite the __get__ class.
3.read-only Data Descriptor also defines __get__,__set__, but this __set__ is just raise attributeerror
The difference between data descriptor and none Data descriptor: The precedence relative to the instance dictionary.
If the instance dictionary has the same name as the descriptor, if the descriptor is a data descriptor, the data descriptor is given precedence, and if the descriptor is a non-data descriptor,
The attributes in the dictionary are used first. Example of this rule in practice: If the instance has the same name as the method and the attribute, Python takes precedence over the attributes in the instance dictionary.
Because the implementation of an instance function is a non-data descriptor.
Second, through instance access attributes:
Instance.a
__getattribute__,__getattr__,__get__ and __dict__ are all related to property access and their precedence:
1. When the __getattribute__ method is defined in the class (type (instance)), the __getattribute__ is called unconditionally. So in the __getattribute__ method, you cannot appear self.__attr__ This invocation, which causes unrestricted recursion
2. If the accessed attr exists and the attr is of type (instance) or is a parent of type (Instace) (super class is not metaclass), And this attr is a descriptor, then the call will continue to class.__get__ accordingly. Nutshell:
2.1 This attr is a descriptor that calls this property __get__
2.2 This attr is not a descriptor, call __dict__[attr]
3. If the property is not defined in the class, call __getattr__
4. Otherwise, throw an exception attributeerror
- Experiment one: In SELF.__DICT__ can obtain a compliance with the descriptor attr, this attr is not a descriptor, so do not comply with the descriptor rules
Class Datadescriptor (object):d EF __get__ (self,obj,owner):p rint ("datadescriptor.__get__", Self,obj,owner) return 2class A (object):p Assclass B (a):d ef __init__ (self): Self.datadescriptor=datadescriptor () a=b () print A.datadescriptor #输出 <__main__. Datadescriptor Object at 0x00bd8db0>
- Experiment two: Get attr in class.__dict__, and this attr is a descriptor
Class Datadescriptor (object):d EF __get__ (self,obj,owner):p rint ("datadescriptor.__get__", Self,obj,owner) return 2class A (object):d Atadescriptor=datadescriptor () class B (A):d ef __init__ (self):p assa=b () print a.datadescriptor "output (' datadescriptor.__get__ ', <__main__. Datadescriptor object at 0x00bd8cf0> <__main__. B object at 0x00bd8d50>, <class ' __main__. B ' >) '
- Experiment three: __getattribute__ return non-descriptor
Class Datadescriptor (object):d EF __get__ (self,obj,owner):p rint ("datadescriptor.__get__", Self,obj,owner) return 2class A (object):d Atadescriptor=datadescriptor () class B (A):d ef __init__ (self):p assdef __getattribute__ (self,name): Print ("b.__getattribute__ name=", name) return "ABC" A=B () print a.datadescriptor ' ' Output: (' b.__getattribute__ Name= ', ' Datadescriptor ') ABC '
- Experiment four: __getattribute__ return to descriptor, obey descriptor rules
def __get__ (Self,obj,owner):p rint ("datadescriptor.__get__", Self,obj,owner) return 2class A (object):d atadescriptor= Datadescriptor () class B (A):d ef __init__ (self):p assdef __getattribute__ (self,name):p rint ("b.__getattribute__ Name= ", name) return type (self). Datadescriptora=b () Print A.datadescriptor" Output: (' b.__getattribute__ name= ', ' Datadescriptor ') (' datadescriptor.__get__ ', <__main__. Datadescriptor object at 0x00bd8cb0>, None, <class ' __main__. B ' >) 2 "
- Experiment five, in the case where attr is not found
This is a special case, return none in __getattribute__ or no return statement, will not be called, only in __getattribute__ raise Attributeerror (), only call __ getattr__, if no __getattribute__ is defined, the VM defaults to raise Attributeerror () If attribute is not found.
Code 1
Class Datadescriptor (object):d EF __get__ (self,obj,owner):p rint ("datadescriptor.__get__", Self,obj,owner) return 2class A (object):d Atadescriptor=datadescriptor () class B (A):d ef __init__ (self):p assdef __getattribute__ (self,name): Print ("b.__getattribute__ name=", name) raise Attributeerror () #return nonedef __getattr__ (self,name):p rint ("B._ _getattr__ name= ", name) return" Not Found "a=b () Print A.datadescriptor ' ' defines __getattribute__, but raise Attributeerror, So will continue to call to __getattr__, no no raise attributeerror, no matter what __getattribute__ did, will not continue to call __getattr__ ""
Code 2
Class Datadescriptor (object):d EF __get__ (self,obj,owner):p rint ("datadescriptor.__get__", Self,obj,owner) return 2class A (object):d Atadescriptor=datadescriptor () class B (A):d ef __init__ (self):p ass#def __getattribute__ (self,name) : #print ("b.__getattribute__ name=", name) #raise attributeerror () #return nonedef __getattr__ (self,name):p rint (" b.__getattr__ name= ", name) return" Not Found "a=b () Print a.zz ' ' cannot find ZZ This ATTR,VM default raise Attributeerror, automatically calls __getattr __‘‘‘
Third, access attributes through class
Using class object to get attr is conceptually the same as getting properties through instance, instance class is a class object, and class object class should be the class Metaclass
When attr is not found in the Dict of class object, it is turned to the Metaclass dict of class.
The rules for accessing properties through Classa.attr are:
- If there is __getattribute__ in Metaclass, the result of the __getattribute__ is returned directly.
- If attr is a descriptor, the result of the __get__ of descriptor is returned directly.
- If attr is a property in Class.dict, the value of attr is returned directly
- If there is no attr in the class and __getattr__ is defined in Metaclass, the __getattr__ in Metaclass is called
- Throws an exception if there is no attr in the class and __getattr__ is not defined in Metaclass attributeerror
Class Metaclass (Type):d atadescriptor=datadescriptor () def __new__ (metaclz,name,bases,attrs):p rint ("Create new class ", Metaclz,name) return type.__new__ (METACLZ, name, bases, Attrs) def __getattr__ (self,name):p rint (" metaclass.__ getattr__ name: ", name) #def __getattribute__ (self,name): #print (" metaclass.__getattribute__ name: ", name) #return name + ' A ' class ClassB (object): __metaclass__=metaclassprint classb.datadescriptorprint classb.ss ' ' Output (' Create new class ', <class ' __main__. Metaclass ', ' ClassB ') (' datadescriptor.__get__ ', <__main__. Datadescriptor object at 0x00bd8ef0>, <class ' __MAIN__.CLASSB ';, <class ' __main__. Metaclass ' > 2 (' metaclass.__getattr__ name: ', ' SS ') None '
The descriptor in Python