0

Object attribute lookup in Python

Posted September 4th, 2015 in Uncategorized by Florentin

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 an object
  • Every object is an instance of a class (example: isinstance(5, int) )
  • Because of that, every class is an instance of a special kind of class called metaclass
  • 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

demo code

Instance attribute look up

Manual excerpt

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__()
    • Break the for each (do not continue searching the same Attribute any further)
  • 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__()
    • If it is NOT a descriptor
      • Return the value
  • If Class has the special method __getattr__
    • Return the result from calling the Class’s special method __getattr__.
  • 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__()
    • Break the for each
  • 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__()
    • Else
      • Return the value
  • 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__()
    • If it is NOT a descriptor
      • Return the value
  • If MetaClass has the special method __getattr__
    • Return the result from calling the MetaClass’s special method __getattr__.
  • Raises an AttributeError

Leave a Reply