Detailed explanation of the principle and usage of metaclass in python

  • 2021-07-01 07:50:41
  • OfStack

In this paper, the principle and usage of metaclass in python are described with examples. Share it for your reference, as follows:

What is metaclass.

metaclass (Metaclass) is the class used to create the class. As we mentioned in the previous article "python Dynamically Creating Classes", we can understand what metaclass is from the following point of view:


MyClass = MetaClass()
MyObject = MyClass()

metaclass is the programming magic in python

At the same time, type was introduced when describing the dynamic creation of class in the previous article "python Dynamic Creation of Classes". It allows you to create a class in the following ways:


MyClass = type('MyClass', (), {})

The fundamental reason is that type is an metaclass, and python uses type to create various classes later. What I don't understand is why it is "type" instead of "Type". It may be considered that str is used to create strings and int is used to create shaping objects, so type is used to create class and object, all in lowercase.

Everything in python is an object. Including int, str, function, class and so on. They are all created from an class, which we can check by looking at the __class__ attribute.


>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

Check the __class__ property


>>> a.__class__.__class__
<type 'type'>
>>> age.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

Whatever you find, it turns out to be "type". In fact, type is an metaclass built into python. Of course, you can create your own metaclass. Here is a very important attribute:

__metaclass__ attribute

When you are writing an class, you can add the __metaclass__ attribute.


class Foo(object):
 __metaclass__ = something...
 [...]

If you do, then python will call metaclass to create Foo class.

python will look for __metaclass__ in your class definition. If found, it will be used to create Foo class. If not, it will be used to create class with type. If it is mentioned in the previous article, it will be used to create class. So, when you


class Foo(Bar):
 pass

pyton will be resolved as follows: whether there is __metaclass__ in Foo, and if so, use metaclass to create an class object named "Foo". If not, see if there is __metaclass__ in its base class Bar, if not, see if there is __metaclass__ in its module layer, and if not, call type to create this class.

The question now is, what can be done in __metaclass__? The conclusion is: something that can create an class. What can create an class is actually type, or a subclass of type (subclass).

Custom metaclass

The main purpose of metaclass is to make some automatic changes when creating classes. For example, to make an inappropriate analogy, we intend to capitalize the properties of all classes in an module. One of the treatments is to use __metaclass__ (stated on module).

We intend to use metaclass to capitalize all attributes. __metaclass__ is not required to be an class, it is also possible to be a method that can be called. Let's start with a simple example


def upper_attr(future_class_name, future_class_parents, future_class_attr):
 """
  Return a class object, with the list of its attribute turned
  into uppercase. """
 # pick up any attribute that doesn't start with '__'
 attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
 # turn them into uppercase
 uppercase_attr = dict((name.upper(), value) for name, value in attrs)
 # let `type` do the class creation
 return type(future_class_name, future_class_parents, uppercase_attr)
__metaclass__ = upper_attr # this will affect all classes in the module
class Foo(): # global __metaclass__ won't work with "object" though
 # but we can define __metaclass__ here instead to affect only this class
 # and this will work with "object" childrend
 bar = 'bip'
print hasattr(Foo, 'bar')
# Out: False
print hasattr(Foo, 'BAR')
# Out: True
f = Foo()
print f.BAR
# Out: 'bip'

Now use one class to handle


# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
  # __new__ is the method called before __init__
  # it's the method that creates the object and returns it
  # while __init__ just initializes the object passed as parameter
  # you rarely use __new__, except when you want to control how the object
  # is created.
  # here the created object is the class, and we want to customize it
  # so we override __new__
  # you can do some stuff in __init__ too if you wish
  # some advanced use involves overriding __call__ as well, but we won't
  # see this
  def __new__(upperattr_metaclass, future_class_name,
        future_class_parents, future_class_attr):
    attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
    uppercase_attr = dict((name.upper(), value) for name, value in attrs)
    return type(future_class_name, future_class_parents, uppercase_attr)

Obviously, this is not very oop's practice, and it directly calls type Method of the parent class instead of calling the __new__ Method, do the following:


class UpperAttrMetaclass(type):
  def __new__(upperattr_metaclass, future_class_name,
        future_class_parents, future_class_attr):
    attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
    uppercase_attr = dict((name.upper(), value) for name, value in attrs)
    # reuse the type.__new__ method
    # this is basic OOP, nothing magic in there
    return type.__new__(upperattr_metaclass, future_class_name,
              future_class_parents, uppercase_attr)

You may notice that upperattr_metaclass, which is actually similar to self The more general methods of self.1 in common class methods are as follows:


class UpperAttrMetaclass(type):
  def __new__(cls, name, bases, dct):
    attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
    uppercase_attr = dict((name.upper(), value) for name, value in attrs)
    return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)

Through the above example, you can understand metaclass, and also understand the __init__ Method, __new__ Method to do 1 hook. Of course, you can also do it in __call__ Make a fuss about it, but more people like to make a fuss about it __init__ Modified inside.

More readers who are interested in Python can check out the topics of this site: "Introduction and Advanced Tutorial of Python Object-Oriented Programming", "Python Data Structure and Algorithm Tutorial", "Summary of Python Function Use Skills", "Summary of Python String Operation Skills", "Summary of Python Coding Operation Skills" and "Introduction and Advanced Classic Tutorial of Python"

I hope this article is helpful to everyone's Python programming.


Related articles: