Understanding and basic usage of metaclasses

metaclass

"Metaclasses are deep magic, and 99% of users shouldn't have to worry about them at all.
If you want to figure out whether you need to use metaclasses, you don't need them.
Those who actually use metaclasses don't need them. It’s very clear what they need to do, and there’s no need to explain why metaclasses are used at all.” — Tim Peters

what is metaclass

In python, all classes are created based on metaclasses.

class demo(object):
    pass

In python, everything is an object, and classes are also objects, so a class must have a type.

The object here is the base class of all Python class hierarchies, which means that all classes inherit it.

So, what type is object?

print(type(object))

Let’s print it out and take a look:

<class 'type'>

What is shown is the metaclass.

Let’s go back to what we said earlier and add annotations to make it easier to understand.

  • In python, everything is an object, and a class is also an object, so a class must have a type.
  • The class used to create a class is called a metaclass, and the function type is actually a metaclass.
  • Any formal class in python and any class in python3 is an instance of the type metaclass.

It should be noted that we need to distinguish between metaclasses and inherited base classes:

  • type: is a metaclass, all classes are created through type
  • object: the top-level base class. The inherited top-level parent class of all classes is object.
  • Type is the creator Nuwa, and object is the first child created by Nuwa. (Not very accurate, but easy to understand)

Detailed explanation of parameters

type source code

class type(object):
    """
    type(object_or_name, bases, dict)
    type(object) -> the object's type
    type(name, bases, dict) -> a new type
    """

The source code says that type(name, bases, dict)you can define a new metaclass by using it.

Detailed explanation of parameters

  • name: Represents the name of the class to be created. (string type)

  • bases: A tuple of base classes (or a metaclass containing a base class) from which the class inherits. (tuple type)

  • dict: Class attributes and methods. (dictionary type)

Comparison between custom class and metaclass creation

We create the class ourselves, the code is as follows

class MyClass(object):
    x = 42

obj = MyClass()
print(obj.x) # 输出42

Here, the class name MyClassinherits the base class objectand the attributes arex=42

Then we will construct a class with the same functions as the above class according to the way metaclass creates classes.

MyClass = type('MyClass',(object,),{
    
    "x":42})
obj = MyClass()
print(obj.x) #输出42

Since object is the base class inherited by default, basesthe parameters can be empty. The code is as follows:

MyClass = type('MyClass',(),{
    
    "x":42})
obj = MyClass()
print(obj.x) #输出42

Why use metaclasses when we can create our own classes?

Don't ask, just go back to what Tim Peters said at the beginning of the article.

go deeper

From the above example, we know that objectit is the base class of all classes, typebut the class that creates the class. So if I modify the base class, will the created class automatically inherit the base class I modified?

define a metaclass

  • Declare a class and inherit from typeit.
  • Define methods in metaclasses __new__that are used to create new classes.
  • In __new__methods, you can customize the behavior, properties and methods of the class.
class MyMeta(type):
    def __new__(meta, name, bases, attrs):
        # 自定义类的行为
        print("想不到吧,我才是基类")
        # 创建并返回新的类
        return super().__new__(meta, name, bases, attrs)

Create classes dynamically using metaclasses

When creating a class, you can use metaclassparameters to specify the metaclass to use.

Declare a normal class and metaclassset the parameters to the defined metaclass, which metaclassdefaults totype

class MyClass(metaclass=MyMeta):
    # 类的定义
    hh = 123
    print("呜呜呜,我就是一个普通的类,可我的元类不是type了,而是自定义的")

Run the above code:

aa = MyClass()
print(aa.hh)
#输出结果如下>>>
呜呜呜,我就是一个普通的类,可我的元类不是type了,而是自定义的
想不到吧,我才是基类
123

If we do not specify the metaclass as a custom metaclass, the output will not contain the unexpected line.

class MyClass2():
    # 类的定义
    h = 123
    print("呜呜呜,我就是一个普通的类,可我的元类不是type了,而是自定义的")

aa = MyClass2()
print(aa.h)
#输出结果如下>>>
呜呜呜,我就是一个普通的类,可我的元类不是type了,而是自定义的
123

Write a simple logger using metaclasses

class LogMeta(type):
    def __new__(meta, name, bases, attrs):
        # 检查属性中的日志消息
        logs = {
    
    }
        for key, value in attrs.items():
            if isinstance(value, str) and value.startswith("LOG "):
                logs[key] = value
        
        # 添加日志方法
        for key in logs.keys():
            def log_func(self, message):
                print(f"Log {
      
      key}: {
      
      message}")
            attrs[key] = log_func
        
        # 创建并返回新的类
        return super().__new__(meta, name, bases, attrs)


class MyLogger(metaclass=LogMeta):
    LOG_INFO = "LOG INFO"
    LOG_ERROR = "LOG ERROR"
    LOG_WARNING = "LOG WARNING"

In the above example, we created a LogMetametaclass that checks the log messages in the class properties and dynamically creates the log methods. We then LogMetacreated a MyLoggerclass using metaclasses and defined several log messages in it. Finally, we can use MyLoggerthe class to create objects and call logging methods.

logger = MyLogger()
logger.LOG_INFO("This is an informational message.")
logger.LOG_ERROR("An error occurred.")

When we run the above code, it will output the following:

Log LOG_WARNING: This is an informational message.
Log LOG_WARNING: An error occurred.

In the next section, we will talk about how to use it in our testing framework to dynamically create test cases.

Guess you like

Origin blog.csdn.net/qq_46158060/article/details/132021582