Resources
descriptors
- https://docs.python.org/2/howto/descriptor.html
- http://stackoverflow.com/questions/3798835/understanding-get-and-set-and-python-descriptors
python 2 docs
- https://docs.python.org/2/reference/datamodel.html?highlight=metaclass
great insights
- http://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/
__mro__
(Method Resolution Order)
- http://stackoverflow.com/questions/2010692/what-does-mro-do-in-python
- http://stackoverflow.com/questions/1848474/method-resolution-order-mro-in-new-style-python-classes
simulate a __getattribute__
call
- http://codereview.stackexchange.com/questions/92497/simulate-object-getattribute-in-python
Definitions
- Everything in Python is an object (i.e. classes, modules, the numbers, the strings, etc)
- A
class
is also anobject
- Every
object
is an instance of aclass
(example: isinstance(5, int) ) - Because of that, every
class
is an instance of a special kind of class calledmetaclass
- An instance is created by calling a class object
- A non-data descriptor is an object following the data descriptor protocol as described in the docs
- A data descriptor is a descriptor which defined both the
__set__
AND__get__
methods __mro__
is a tuple of classes that are considered when looking for base classes during method resolution
Code snippet
Instance attribute look up
The implementation works through a precedence chain that gives data descriptors priority over instance variables, instance variables priority over non-data descriptors, and assigns lowest priority to getattr() if provided.
Given a Class “C” and an Instance “c” where “c = C(…)”, calling “c.name” means looking up an Attribute “name” on the Instance “c” like this:
- Get the Class from Instance
- Call the Class’s special method
__getattribute__
. All objects have a default__getattribute__
Inside __getattribute__
- Get the Class’s
__mro__
as ClassParents - For each ClassParent in ClassParents
- If the Attribute is in the ClassParent’s
__dict__
- If is a data descriptor
- Return the result from calling the data descriptor’s special method
__get__()
- Return the result from calling the data descriptor’s special method
- Break the for each (do not continue searching the same Attribute any further)
- If the Attribute is in the ClassParent’s
- If the Attribute is in Instance’s
__dict__
- Return the value as it is (even if the value is a data descriptor)
- For each ClassParent in ClassParents
- If the Attribute is in the ClassParent’s
__dict__
- If is a non-data descriptor
- Return the result from calling the non-data descriptor’s special method
__get__()
- Return the result from calling the non-data descriptor’s special method
- If it is NOT a descriptor
- Return the value
- If the Attribute is in the ClassParent’s
- If Class has the special method
__getattr__
- Return the result from calling the Class’s special method
__getattr__
.
- Return the result from calling the Class’s special method
- Raises an
AttributeError
Things to remember (from the manual)
- descriptors are invoked by the getattribute() method
- overriding getattribute() prevents automatic descriptor calls
- getattribute() is only available with new style classes and objects
- object.getattribute() and type.getattribute() make different calls to get().
- data descriptors always override instance dictionaries.
- non-data descriptors may be overridden by instance dictionaries.
Class attribute look up
Given a MetaClass “M” and a Class “C” instance of the Metaclass “M”, calling “C.name” means looking up an Attribute “name” on the Class “C” like this:
- Get the Metaclass from Class
- Call the Metaclass’s special method
__getattribute__
Inside __getattribute__
- Get the Metaclass’s
__mro__
as MetaParents - For each MetaParent in MetaParents
- If the Attribute is in the MetaParent’s
__dict__
- If is a data descriptor
- Return the result from calling the data descriptor’s special method
__get__()
- Return the result from calling the data descriptor’s special method
- Break the for each
- If the Attribute is in the MetaParent’s
- Get the Class’s
__mro__
as ClassParents - For each ClassParent in ClassParents
- If the Attribute is in the ClassParent’s
__dict__
- If is a (data or non-data) descriptor
- Return the result from calling the descriptor’s special method
__get__()
- Return the result from calling the descriptor’s special method
- Else
- Return the value
- If the Attribute is in the ClassParent’s
- For each MetaParent in MetaParents
- If the Attribute is in the MetaParent’s
__dict__
- If is a non-data descriptor
- Return the result from calling the non-data descriptor’s special method
__get__()
- Return the result from calling the non-data descriptor’s special method
- If it is NOT a descriptor
- Return the value
- If the Attribute is in the MetaParent’s
- If MetaClass has the special method
__getattr__
- Return the result from calling the MetaClass’s special method
__getattr__
.
- Return the result from calling the MetaClass’s special method
- Raises an
AttributeError