简体   繁体   中英

How to determine the metaclass of a class?

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?)

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.

Here's a helper function that can be used in such scenario:

def validate_abstract_methods(obj):
    abstract_methods = []
    for name in dir(obj):
        value = getattr(obj, name, None)
        if value is not None and getattr(value, '__isabstractmethod__', False):
            abstract_methods.append(name)
    if abstract_methods:
        abstract_methods.sort()
        raise TypeError(f"Can't instantiate abstract class {obj.__class__.__name__} with abstract methods {', '.join(abstract_methods)}")

This function roughly does the same thing as abc.ABC class - you just need to call it from your class' __init__ method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM