Note 1
Just to be clear:
Foo does not inherit from FooMeta.
FooMeta is not a super-class of Foo
super will not work.
Note 2
Now that note (1) is out of the way, if you want to access a metaclass method from inside of a method of an instance of the metaclass, you can do it like this:
class FooMeta(type):
_foo = 5
def get_foo(cls):
print("`get_foo` from `FooMeta` was called!")
class Foo(metaclass=FooMeta):
@classmethod
def bar(Foo):
FooMeta = type(Foo)
FooMeta_dot_getfoo = FooMeta.get_foo
FooMeta_dot_getfoo(Foo)
def baz(self):
Foo = type(self)
FooMeta = type(Foo)
FooMeta_dot_getfoo = FooMeta.get_foo
FooMeta_dot_getfoo(Foo)
Foo.bar()
foo = Foo()
foo.baz()
The output is:
`get_foo` from `FooMeta` was called!
`get_foo` from `FooMeta` was called!
Note 3
If you have a classmethod with the same name as a method in the metaclass, why does the metaclass method NOT get called? Consider the following code:
class FooMeta(type):
def get_foo(cls):
print("META!")
class Foo(metaclass=FooMeta):
@classmethod
def get_foo(cls):
print("NOT META!")
Foo.get_foo()
The output is NOT META! In the following discussion, assume that:
foo is instance of Foo
Foo is instance of FooMeta
For the first time in this post, I will have pseudo code, not python. Don't try to run the following. __getattribute__ sorta looks like the following:
class FooMeta(type):
def get_foo(Foo):
print("META!")
class Foo(metaclass=FooMeta):
@classmethod
def get_foo(Foo):
print("NOT META!")
def __getattribute__(foo, the string "get_foo"):
try:
attribute = "get_foo" from instance foo
except AttributeError:
attribute = "get_foo" from class Foo
# Begin code for handling "descriptors"
if hasattr(attribute, '__get__'):
attr = attribute.__get__(None, Foo)
# End code for handling "descriptors"
return attribute
foo = Foo()
foo.get_foo() # prints "NOT META!"
get_foo = Foo.__getattribute__(foo, "get_foo")
get_foo.__call__()
You can actually ignore the stuff which says, "code for handling "descriptors"." I only included that for completeness.
Note that nowhere does __getattribute__ say, "get get_foo from the meta class."
- First, We try to get
get_foo from the instance. Maybe get_foo is a member variable. Maybe one instance has get_foo = 1 and another instance has get_foo = 5 The computer does not know. The computer is stupid.
- The computer realizes that the instance doesn't have a member variable named
get_foo. It then says, "ah ha! I bet that get_foo belongs to the CLASS." So, it looks there, and lo-and-behold, there it is: Foo has an attribute named get_foo. FooMeta also has an attribute called get_foo, but who cares about that.
Something to focus on is that:
Foo has an attribute named get_foo
MetaFoo has an attribute named get_foo
They both have attributes named get_foo, but Foo and MetaFoo are different objects. It's not as if the two get_foos are shared. I can have obj1.x = 1 and obj2.x = 99. No problem.
FooMeta has its own __getattribute__ method. Before I talked about Foo.__getattribute__, but now let's talk about the MeTa __getattribute__
class FooMeta(type):
def get_foo(Foo):
print("META!")
def __getattribute__(Foo, the string "get_foo"):
try: # LINE 1
attribute = "get_foo" from class Foo # LINE 2
except AttributeError: # LINE 3
attribute = "get_foo" from class FooMeta # LINE 4
# LINE 5
# Begin code for handling "descriptors"
if hasattr(attribute, '__get__'):
attr = attribute.__get__(None, Foo)
# End code for handling "descriptors"
return attribute
class Foo(metaclass=FooMeta):
@classmethod
def get_foo(Foo):
print("NOT META!")
Foo.get_foo()
get_foo = FooMeta.__getattribute__(Foo, "get_foo")
get_foo.__call__()
The order of events:
- Lines 1 and 2 happen
- Lines 3, 4, & 5 do not happen
- You can ignore the stuff about descriptors, because none of the different
get_foos in this problem have a __get__ method
Okay now! Why only lines 1 & 2? Because you made a @classmethod silly! We check Foo to see if it has a get_foo and it does! Why check for class attributes if we find an instance attribute first? We always check to see if an attribute belong to the instance (Foo) first-and-foremost before checking if maybe there happens to be only one copy of a static member variable belonging to the class (FooMeta) and shared by all of the instances.
Note that if Foo does not have a get_foo then FooMeta.__getattribute__(Foo, "get_foo") will return get_foo from the metaclass because the first attempt (getting it from the instance) failed. You kinda blocked that option out by giving the instance something of the same name as the class's static member variable.
class K:
im_supposed_to_be_shared = 1
def __init__(self, x):
# NOPE!
self.im_supposed_to_be_shared = x
# maybe try type(self)
obj1 = K(14)
obj2 = K(29)
print(obj1.im_supposed_to_be_shared)
print(obj2.im_supposed_to_be_shared)
print(K.im_supposed_to_be_shared)
Prints:
14
29
1
does NOT print:
29
29
29
Note that if you want to set a static class member variable, instance.mem_var = 5 is a very ⱽᵉᴿʸ bad idea. You will give the instance a new member variable, and the class static (shared) member variable will be shadowed. You can fix that with something like this:
def __setattr__(self, attr_name, attr_val):
if hasattr(type(self), attr_name):
setattr(type(self), attr_name, attr_val)
else:
super_class = inspect.getmro(type(self))[1]
super_class.__setattr__(self, attr_name, attr_val)
Then your lil' compy will print:
29
29
29
Note 4
class Foo:
@classmethod
def funky(cls):
pass
is NOT MetaClass.funky = funky. Instead, it's:
def funky(cls)
pass
Funky = classmethod (funky)
... which is almost the same as:
def funky(cls):
pass
funky = lambda self, *args, **kwargs: funky(type(self), *args, **kwargs)
The moral of note 4s story is that classmethod funky is an attribute of Foo and not an attribute of FooMeta
return FooMeta.get_foo(cls) + 1, but it begs the question - metaclasses are for customizing creation of the class. You're not doing that. So, why are you using a metaclass?superis more about traversing the MRO (parent or sibling classes when using inheritance) so I'm not really sure why you're trying to use it to get at a metaclass method in the first place, these are quite unrelated concerns. - wimsuper(FooMeta, Foo)to proxy to an existingget_foomethod at all? Because, in absence of anything else in the inheritance chain, the "next method" here would just be attempting a method resolution ontype. If you wanted to access the metaclassget_foomethod, you just useFooMeta.get_foodirectly. - wim