Python进阶之元类
目录
什么是元类?
元类的调用流程
根据类自定义元类
__new__
方法以及参数----------cls
----------name
----------bases
----------attrs
__call__
方法生成对象的完整代码
什么是元类?
在python面向对象中,我们知道所有的新式类都会继承object类,但是object类又是从何而来呢?是否所有的类在构建之前都会有一个框架呢?
为了方便理解我们直接上代码:
class MyClass(object):passprint(type(MyClass()))
# <class '__main__.MyClass'>
# 类的实例对象属于 MyClassprint(type(MyClass))
# <class 'type'>
# 发现类的对象属于 typeprint(type(int))
# <class 'type'>
# 发现int类也属于 typeprint(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
)创建的,那么元类便是类的模版
大致的调用流程图解:
根据类自定义元类
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__) # 输出:MyMetareturn super().__new__(cls, name, bases, attrs)class My(object, metaclass=MyMeta):def __init__(self, name):self.name = namem = My('张三')
name
class MyMeta(type):def __new__(cls, name, bases, attrs):print(name) # 输出:MyClassif len(name) > 3:print("你好", end='')return super().__new__(cls, name, bases, attrs)class MyClass(object, metaclass=MyMeta):def __init__(self, name):self.name = namem = 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:passclass MyClass(Parent, metaclass=MyMeta):def __init__(self, name):self.name = namem = MyClass('张三')
attrs
class MyMeta(type):def __new__(cls, name, bases, attrs):# 修改属性attrs['modified_attr'] = 100 # 新增键modified_attrprint(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_methodprint(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 123123class MyClass(metaclass=MyMeta):passmy_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):passclass myclass(metaclass=MyMeta):passmy_obj = MYCLASS() # is upper
my_obj2 = myclass() # is not upper
__call__
的执行流程:
生成对象的完整代码
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 instancedef __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='张三')