Python进阶之元类

发布时间:2024年01月09日

Python进阶之元类

目录
什么是元类?
元类的调用流程
根据类自定义元类
__new__方法以及参数
----------cls
----------name
----------bases
----------attrs
__call__方法
生成对象的完整代码

什么是元类?

在python面向对象中,我们知道所有的新式类都会继承object类,但是object类又是从何而来呢?是否所有的类在构建之前都会有一个框架呢?

为了方便理解我们直接上代码:

class MyClass(object):
    pass

print(type(MyClass()))
# <class '__main__.MyClass'>
# 类的实例对象属于 MyClass

print(type(MyClass))
# <class 'type'>
# 发现类的对象属于 type

print(type(int))
# <class 'type'>
# 发现int类也属于 type

print(type.__base__)
# <class 'object'>
# 发现其父类是 object

根据我们已学的知识1和4不难理解,那么这个type又是什么呢?

object,class,type三者之间的关系:

  • object:object类是所有类(class)的父类,包括type类,并且object没有父类

  • class:class继承自object类,同时由type进行实例化。其中,type就是我们所要讲的元类(MetaClass)

  • type:type类是所有类的类型,即为所有类(class)都可由type实例化而来,包括object类,甚至其type本身也是由它初始化的

元类的调用流程

知道了什么是元类,那么他的调用流程也必然是我们需要知道的

在我们平时构造类时,类就是实例化对象的模板__init__初始化的则是对象的属性

那么相同的,类是由元类(MetaClass)创建的,那么元类便是类的模版

大致的调用流程图解:

image-20240108212405346

根据类自定义元类

class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        # 在类创建之前执行一些操作
        # 可以修改类的属性和方法
        # 可以添加额外的属性和方法
        return super().__new__(cls, name, bases, attrs)

class MyClass(metaclass=MyMeta):
    pass

在python中我们需要用到__new__()特殊方法来在对象创建之前让其经过自定义元类MyMeta(type)调用

大白话就是我们自建了一个元类,并且继承type以便调用它的特殊方法,上面的代码中我们重写了__new()__方法,接下来我们只需要在方法中对参数(cls, name, bases, attrs)进行操作即可

__new__方法以及参数

所有参数名

cls

class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        print(cls) # 输出:<class '__main__.MyMeta'>
        print(cls.__name__) # 输出:MyMeta
        return super().__new__(cls, name, bases, attrs)


class My(object, metaclass=MyMeta):
    def __init__(self, name):
        self.name = name


m = My('张三')

name

class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        print(name) # 输出:MyClass
        if len(name) > 3:
            print("你好", end='')
        return super().__new__(cls, name, bases, attrs)


class MyClass(object, metaclass=MyMeta):
    def __init__(self, name):
        self.name = name


m = MyClass('张三')
print(m.name)
# MyClass
# 你好张三

bases

class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        print(bases) # 输出:(<class '__main__.Parent'>,)
        return super().__new__(cls, name, bases, attrs)

class Parent:
    pass

class MyClass(Parent, metaclass=MyMeta):
    def __init__(self, name):
        self.name = name


m = MyClass('张三')

attrs

class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        # 修改属性
        attrs['modified_attr'] = 100 # 新增键modified_attr
        print(attrs) # {'__module__': '__main__', '__qualname__': 'MyClass', 'original_method': <function MyClass.original_method at 0x000001B3618A1800>, 'modified_attr': 100} 


        # 修改方法
        def modified_method(self):
            return "方法2"

        attrs['modified_method'] = modified_method
        print(attrs)

        return super().__new__(cls, name, bases, attrs)


class MyClass(metaclass=MyMeta):
    def original_method(self):
        return "方法1"


# 测试示例
my_obj = MyClass()
print(my_obj.modified_attr)  # 输出: 100
print(my_obj.original_method())  # 输出: 方法1
print(my_obj.modified_method())  # 输出: 方法2

__call__方法

在实例化对象的时候,会自动触发类中的__new__方法执行其中的代码,并且触发__init__方法进行初始化属性。那么此时不得不思考一个问题:为什么类会先调用__new__再调用__init__?是谁在控制这个流程?

-----__call__

调用类就是调用类的 __call__方法,类调用后的运行逻辑,其实都是 call 方法来控制的

__call__方法可以将函数或者类都转换成可调用对象,也就是我们平时调用函数时在后面加的括号:

# 函数中使用__call__
def text():
    print("你好")


text() # 你好
text.__call__() # 你好
# 类中使用__call__
class MyClass():
    def __call__(self, *args, **kwargs):
        print('你好')


my_obj = MyClass()
my_obj() # 你好

在元类中我们可以利用重写__call__方法来满足我们特定的需求:

class MyMeta(type):
    def __call__(cls, *args, **kwargs):
        print("重写__call__方法")
        return 123123


class MyClass(metaclass=MyMeta):
    pass


my_obj = MyClass()
print(my_obj)# 输出:123123



# 用__call__方法判断类名是否为大写
class MyMeta(type):
    def __call__(cls, *args, **kwargs):
        if cls.__name__.isupper():
            print('is upper')
        else:
            print('is not upper')
        return super().__call__(*args, **kwargs)


class MYCLASS(metaclass=MyMeta):
    pass


class myclass(metaclass=MyMeta):
    pass


my_obj = MYCLASS()  # is upper
my_obj2 = myclass()  # is not upper

__call__的执行流程:

image-20240109121001606

生成对象的完整代码

class MyMeta(type):
    def __new__(cls, *args, **kwargs):
        print("type调用了MyMeta的__new__方法,并且返回了一个对象,该对象即MyClass")
        return super().__new__(cls, *args, **kwargs)

    def __call__(cls, *args, **kwargs):
        print("这里调用了MyMeta的__call__方法,在这里可以对MyClass生成的对象a进行额外的操作,最终返回初始化好的对象a")
        instance = super().__call__(*args, **kwargs)
        return instance

    def __init__(cls, what, bases=None, dict=None):
        print("当前接受的cls对象即MyClass,这里不重写直接继承并初始化数据")
        super(MyMeta, cls).__init__(what, bases, dict)


class MyClass(metaclass=MyMeta):
    def __init__(self, age, name=None):
        self.name = name


# 创建一个可调用对象
a = MyClass(18, name='张三')
t__(cls, what, bases=None, dict=None):
        print("当前接受的cls对象即MyClass,这里不重写直接继承并初始化数据")
        super(MyMeta, cls).__init__(what, bases, dict)


class MyClass(metaclass=MyMeta):
    def __init__(self, age, name=None):
        self.name = name


# 创建一个可调用对象
a = MyClass(18, name='张三')
文章来源:https://blog.csdn.net/AZURE060606/article/details/135479833
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。