[Python Advanced] Metaclass metaclass (type class) and object class

4.9 Metaclass metaclass (type class) and object class

4.9.1 object class

In object-oriented programming, a class is a blueprint or template for an object. A class defines a set of properties and methods from which new objects can be created. Since each object is created based on a class, they share the same properties and methods.
The object class is a very important class, it is the base class of all classes. In the previous version of Python, if we need to create our own class, we need to manually inherit the object class.
In Python, almost all objects can use its methods and attributes, including objects of built-in types such as integers, strings, and tuples. For example:

x = 5
print(x.__class__) # <class 'int'>
print(x.__dir__()) # [......'bit_length', 'to_bytes', 'hex'......]

Here we use the __class__() magic method to print the class to which the variable belongs, and call the __dir__() magic method to print all available attributes and methods of the variable.

4.9.2 Metaclass metaclass (type class)

In Python, a metaclass is a class used to create other classes. Simply put, it can be seen as a factory of classes, controlling the creation process of classes. The most common usage scenario of metaclasses is to customize classes, such as dynamically adding attributes or methods.
class type is the metaclass of all classes (metaclass). A metaclass is a "class" of classes. They allow new classes to be defined just like classes define instance objects. Using metaclasses, you can control how a class is created. As long as classes derived from class type are metaclasses, and type is the default metaclass.
In Python, there are 2 ways to define ordinary classes: 静态定义and 动态生成.

from icecream import ic


class A(int):  # 静态定义
    aa = 1


B = type('C', (int,), {
    
    'bb': 2})  # 动态生成

ic(type(A), type(A()))
ic(type(B), type(B()))
ic(A.aa, A().aa)
ic(B.bb, B().bb)

14:10:06|> type(A): <class ‘type’>, type(A()): <class ‘main.A’>
14:10:06|> type(B): <class ‘type’>, type(B()): <class ‘main.C’>
14:10:06|> A.aa: 1, A().aa: 1
14:10:06|> B.bb: 2, B().bb: 2

We see that using class to define a class is actually defining it by calling the type function. Both are almost equivalent.

from icecream import ic


class M(type):
    def __new__(mcs, *args, **kwargs):
        print(mcs, args, kwargs)
        return type.__new__(mcs, *args, **kwargs)


class A(metaclass=M):  # 相当于A = M('A', (), {}),而M则为A的元类
    pass

<class ‘main.M’> (‘A’, (), {‘module’: ‘main’, ‘qualname’: ‘A’}) {}

Define class M as above, M is the metaclass of A, and A is the object of M. And a is the object of A. When creating A through M, M's __new__() method and __init__() method will be run in sequence.
In a general class, only the __new__() method will be run, unless there is no __new__() method, the __init__() method will be run.
At this time, we can do some custom operations by rewriting the __new__() method and __init__() method in M.
The __init__() method defined in the metaclass, this method is called after the class is created through the metaclass.

from icecream import ic
import random


class M(type):
    def __new__(mcs, *args, **kwargs):
        print(mcs, args, kwargs)
        return type.__new__(mcs, *args, **kwargs)

    def __init__(cls, *args, **kwargs):
        print(cls, args, kwargs)
        cls.random_id = random.randint(0, 100)
        return type.__init__(cls, *args, **kwargs)


class A(metaclass=M):
    pass

ic(A.random_id)

<class ‘main.M’> (‘A’, (), {‘module’: ‘main’, ‘qualname’: ‘A’}) {}
<class ‘main.A’> (‘A’, (), {‘module’: ‘main’, ‘qualname’: ‘A’}) {}
15:38:07|> A.random_id: 27

In the metaclass, you can also continue to define the __call__() method. This method is called when the class is created through the metaclass and then instantiated through the class.
Note: In ordinary classes, the __call__() method will not be run until called by the instantiated object of the class.

class M(type):
    def __new__(mcs, *args, **kwargs):
        print(mcs, args, kwargs)
        return type.__new__(mcs, *args, **kwargs)

    def __call__(cls, *args, **kwargs):
        print('call')
return type.__call__(cls, *args, **kwargs)


class A(metaclass=M):
    pass


a = A()

<class ‘main.M’> (‘A’, (), {‘module’: ‘main’, ‘qualname’: ‘A’}) {}
call

Metaclasses are generally used to solve problems that inheritance cannot handle. It allows you to more flexibly grasp the logic in the middle of creating a class. For example, we can add or delete methods through the members defined in the class.
实例一:禁止自定义类中出现‘test_’开头的方法

from icecream import ic


class M(type):
    def __new__(mcs, *args, **kwargs):
        print(mcs, args, kwargs)
        for key in args[2]:
            if key.startswith('test_'):
                raise ValueError()
        return type.__new__(mcs, *args, **kwargs)


class A(metaclass=M):
    def test_case(self):
        pass

<class ‘main.M’> (‘A’, (), {‘module’: ‘main’, ‘qualname’: ‘A’, ‘test_case’: <function A.test_case at 0x000001729106CD30>}) {}
Traceback (most recent call last):
File “E:/t3.py”, line 13, in
class A(metaclass=M):
File “E:/BaiduNetdiskWorkspace/FrbPythonFiles/Project/t3.py”, line 9, in new
raise ValueError()
ValueError

示例二:通过元类创建的类具有某些属性

from icecream import ic


class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        attrs["author"] = "China"
        attrs["version"] = (1, 0)
        ic(cls, name, bases)
        return super().__new__(cls, name, bases, attrs)


class MyClass(metaclass=MyMeta):
    pass


print(MyClass.author)  # China
print(MyClass.version)  # (1, 0)

China
(1, 0)
16:07:04|> cls: <class ‘main.MyMeta’>, name: ‘MyClass’, bases: ()

In this example, MyMeta is a metaclass that inherits from the type class. The new method is overridden in MyMeta and will be called when the class is created. In the new method, add the author and version number to the attrs parameter passed in.
MyClass is a class created using the MyMeta metaclass, which is specified in the class definition via the metaclass parameter. When the program is executed, the MyClass class will be created, and the new method of MyMeta will be called first , and then a new class object will be returned. Ultimately, the author and version properties of the MyClass object will be "China" and (1, 0) respectively.
实例三:通过元类实现单例

class Singleton(type):
    _instance = None

    def __call__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance

class MyClass(metaclass=Singleton):
    pass

a = MyClass()
b = MyClass()

print(a is b)  # True

This Python code implements a singleton pattern, ensuring that only one instance of the class exists throughout the program.
First, a Singleton type is defined. This class implements the construction method call(), and judges whether an instance exists currently through the cls._instance variable. If not, create a new instance and store it in the cls._instance variable. ; If an instance already exists, return the instance directly.
Then we define another class MyClass, and set its metaclass to Singleton, so that MyClass becomes a singleton class, and the object created by calling a = MyClass() is actually the same as the object created by b = MyClass() an instance.
Finally, output the result of a is b, since both a and b actually point to the same instance, the result is True.

4.9.3 Relationship between type class and object class

The metaclass type specifies what the base class of all classes, object, should look like, and the instance object generated according to the model of the metaclass type naturally becomes a class. But type itself is also a class, and object precedes type, because the first line of the execution result of 'help(object)' is 'class object', object does not inherit any class; and the execution result of 'help(type)' The first line is 'class type(object)', indicating that type inherits object. Although it is a class of a class, the identity of 'class' also makes it have to conform to the basic form of a class. The relationship between type, object, and instance is as follows:
insert image description here

It can be seen that the relationship between type and object can be summarized as follows:
1. All classes inherit from object (except object)
2. All classes are instances of type (including type itself)

Guess you like

Origin blog.csdn.net/crleep/article/details/131931418