24

I have a class object, cls. I want to know its metaclass. How do I do this?

(If I wanted to know its parent classes, I would do cls.__mro__. Is there something like this to get the metaclass?)

martineau
  • 119,623
  • 25
  • 170
  • 301
Pro Q
  • 4,391
  • 4
  • 43
  • 92
  • 6
    Just `type(cls)` – vaultah May 08 '17 at 17:26
  • @Jim IMO a good answer would explain why the type of a class object is the metaclass it was created with. – vaultah May 08 '17 at 17:31
  • If OP's question is reformatted in a way that actually asks for a *why* and not a *how* then sure @vaultah. As it currently stands I don't personally think it should be re-opened. Feel free to do so if you still believe it's warranted. – Dimitris Fasarakis Hilliard May 08 '17 at 17:34
  • Everything is an object in python. Classes are objects too. Every object has a class associated with it, you can get it by `type(myobject)`. Metaclass is just a class of a class. – hello world May 08 '17 at 17:42
  • There are specific things to class typing that I don't think are covered in the dup. question, and even if they are, people asking about "finding metaclasses" won't automatically search for "finding an object's type" - the text explaining the two things are the same belongs in an answer to _this_ question. (that is why I asked it to be re-openned) – jsbueno May 11 '17 at 14:27

1 Answers1

19

Ok - so, a class's metaclass is just its own "type", and can be given by type(cls) and other means such as cls.__class__.

In Python 3.x there are no further ambiguities - as the syntax for creating a metaclass just passes it as a named parameter on the class declaration statement anyway.

However, the syntax used for creating a metaclass in Python 2.x generates a side-effect that is worth noting.

Upon doing

class A(object):
    __metaclass__ = MyMeta

The __metaclass__ attribute is set to that value in the actual class, even if the actual metaclass is another one.

Consider:

def class_pre_decorator(name, bases, namespace):
     # do something with namespace
     return type(name, bases, namespace)

This is a callable that can be used in the metaclass declaration of both Python 2 and 3 - and it is valid. After resolving, the actual metaclass in both cases will simply be type. However, in Python 2.x, cls.__metaclass__ will point to the callable class_pre_decorator, even tough type(cls) returns type, which is the correct metaclass.(Note that using callables in this way, they will not be used agian when the class is further subclassed)

There is no way in Python 3 to guess the callable actually used to instantiate a class if it gives no other hint (like setting an attribute on the class) that it was used:

# python 2
class A(object):
   __metaclass__ = class_pre_decorator

On the console:

In [8]: type(A)
Out[8]: type

In [9]: A.__metaclass__
Out[9]: <unbound method A.class_pre_decorator>

and

# Python 3
class A(metaclass=class_pre_decorator):
    pass

And trying to read A.__metaclass__ will simply raise an AttributeError.

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • I don't think your final example is very clear. Because of the change in syntax for specifying a metaclass between Python 2 and 3, `class_pre_decorator` is **not** the metaclass of `class A(object)`—defining a class `__metaclass__` attribute like that in Python 3 has no affect on what metaclass gets used (regardless of what kind of value, callable or not, it's given). – martineau Jul 02 '18 at 01:28