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 MyClass
inherits the base class object
and 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, bases
the 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 object
it is the base class of all classes, type
but 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
type
it. - 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 metaclass
parameters to specify the metaclass to use.
Declare a normal class and metaclass
set the parameters to the defined metaclass, which metaclass
defaults 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 LogMeta
metaclass that checks the log messages in the class properties and dynamically creates the log methods. We then LogMeta
created a MyLogger
class using metaclasses and defined several log messages in it. Finally, we can use MyLogger
the 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.