Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

methods of metaclasses on class instances

I was wondering what happens to methods declared on a metaclass. I expected that if you declare a method on a metaclass, it will end up being a classmethod, however, the behavior is different. Example

>>> class A(object):
...     @classmethod
...     def foo(cls):
...         print "foo"
... 
>>> a=A()
>>> a.foo()
foo
>>> A.foo()
foo

However, if I try to define a metaclass and give it a method foo, it seems to work the same for the class, not for the instance.

>>> class Meta(type): 
...     def foo(self): 
...         print "foo"
... 
>>> class A(object):
...     __metaclass__=Meta
...     def __init__(self):
...         print "hello"
... 
>>> 
>>> a=A()
hello
>>> A.foo()
foo
>>> a.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'foo'

What's going on here exactly ?

edit: bumping the question

like image 258
Stefano Borini Avatar asked Feb 11 '10 06:02

Stefano Borini


2 Answers

You raise a good point.

Here is a good referenceto get a better understanding of the relations between objects, classes and metaclasses:

I also find this reference on descriptors to be quite enlightening about the mechanism of look-up in python.

But I can't say I understand why a.foo fails when A.foo succeeds. It seems that when you look up an attribute of an object, and python does not find it there, it does not exactly look up the attribute in the class, because if it did, it would find A.foo.

EDIT:

Oh! I think I got it. It is due to how inheritance works. If you consider the schema provided by the above link, it looks like this:

alt text

Schematically, it boils down to:

type -- object
  |       |
Meta --   A  -- a

Going left means going to the class of a given instance. Going up means going to the parent.

Now the inheritance mechanism makes the look-up mechanism make a right turn in the schema above. It goes a → A → object. It must do so in order to follow the inheritance rule! To make it clear, the search path is:

 object
   ^
   |
   A  <-- a

Then, clearly, the attribute foo will not be found.

When you lookup for the attribute foo in A, however, it is found, because the lookup path is:

type
  ^
  |       
Meta <--   A 

It all makes sense when one thinks of how inheritance works.

like image 182
Olivier Verdier Avatar answered Sep 19 '22 18:09

Olivier Verdier


The rule is like this: when searching for an attribute on an object, the object's class and its parent classes are considered as well. An object's class's metaclass, however, is not considered. When you access an attribute of a class, the class's class is the metaclass, so it is considered. The fallback from object to its class does not trigger a "normal" attribute lookup on the class: for instance, descriptors are called differently whether an attribute is accessed on an instance or its class.

Methods are attributes that are callable (and have a __get__ method that makes 'self' be passed automatically.) That makes it so that methods on the metaclass are like classmethods if you call them on the class, but not available on the instance.

like image 42
Thomas Wouters Avatar answered Sep 21 '22 18:09

Thomas Wouters