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
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:
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With