自定义元类
什么是元类
在python中万物皆对象,类也是一个对象,自定义的类或是python中内置的类都是由元类(type)实例化来的。因此元类也是一种类。
元类 ——实例化——> 类 ——实例化——> 对象
元类实例化类
使用元类实例化类需要三个参数:类名,基类(即继承于哪些父类),类名称空间(由类子代码执行得到)
type(class_name, class_bases, class_dict) # type是内置元类,调用type可实例化一个自定义的类
原类实例化流程(与类实例化对象的过程一致):
- 调用__new__方法产生一个空对象(类)
- 调用__init__方法初始化对象(类)
- 返回初始化后的对象(类)
_new_
__new__方法会在类实例化过程中首先被调用,生成并返回一个空对象
class MyType(type): # 继承type
def __new__(cls, *args, **kwargs):
print("MyType.__new__")
return super().__new__(cls, *args, **kwargs) # 使用父类即type生成空对象
_init_
__init__用于初始化空对象,在自定义元类中,通常会传入四个参数:self(即实例化的对象本身),类名,基类,类名称空间。后三个参数是调用自定义元类实例化类所需的三个参数,可在此构造函数中对类名,基类,类名称空间做一些限制或后处理。
def __init__(self, class_name, class_bases, class_dict):
if '_' in class_name:
raise NameError("类名不能有下划线!") # 限制类名中不能出现下划线
if not class_dict.get('__doc__'):
raise SyntaxError('定义类必须写注释!') # 限制类中必须有注释文档,如有注释文档,类名称空间中有'__doc__'键
_call_
类可以被调用来实例化一个对象,因此自定义元类必须要有__call__方法,它在对象被调用时触发执行。
# 对象可以被调用执行,产生这个对象的类必须有__call__方法
class Dog(metaclass=MyType):
def __init__(self, name, color):
self.name = name
self.color = color
def info(self):
print('name: {}, color: {}'.format(self.name, self.color))
def __call__(self, *args, **kwargs):
print("Dog.__call__")
return "wang"
def __new__(cls, *args, **kwargs):
obj = super().__new__(cls) # 使用父类,即object中的__new__方法来生成一个空对象
return obj
dog1 = Dog('ha', 'white')
print(dog1()) # Dog.__call__, wang, 在调用对象时触发类的__call__方法
同理,Dog这个类可以被调用实例化对象,说明自定义元类中也有__call__方法。
别忘了,类实例化为对象的三个步骤:生成空对象,初始化对象,返回初始化后的对象,即这些过程被封装在元类中的__call__方法中。
def __call__(self, *args, **kwargs):
dog_obj = self.__new__(self) # 调用Dog中的__new__方法产生一个空对象
self.__init__(dog_obj, *args, **kwargs) # 调用Dog中的__init__方法来初始化对象,因此第一个参数时__new__出来的空对象
# 修改对象的属性名
new_dic = {}
for k in dog_obj.__dict__:chu
new_dic[f'New_{k}'] = dog_obj.__dict__[k]
dog_obj.__dict__ = new_dic
return dog_obj # 返回对象
dog1 = Dog('ha', 'white')
print(dog1.__dict__) # {'New_name': 'ha', 'New_color': 'white'}
完整代码
自定义元类流程梳理:
- 继承type类
- 自定义元类本质上也是一个类,实例化生成一个类,因此会先调用__new__方法产生空对象
- 再调用__init__方法初始化空对象
- 使用自定义元类实例化出来的类可被调用,因此自定义元类中有__call__方法
- 实例化出来的类也可实例化为一个对象,因此__call__方法中包含生成空对象,初始化空对象并返回这三个步骤。
# 产生一个dog对象需要调用Dog,即Dog('ha', 'white'),会触发自定义元类MyType中的__call__方法
class MyType(type): # 继承type
def __init__(self, class_name, class_bases, class_dict):
if '_' in class_name:
raise NameError("类名不能有下划线!")
if not class_dict.get('__doc__'):
raise SyntaxError('定义类必须写注释!')
# print(self.__bases__)
def __new__(cls, *args, **kwargs):
print("MyType.__new__")
return super().__new__(cls, *args, **kwargs)
def __call__(self, *args, **kwargs):
dog_obj = self.__new__(self) # 调用Dog中的__new__方法产生一个空对象
self.__init__(dog_obj, *args, **kwargs) # 调用Dog中的__init__方法来初始化对象,因此第一个参数时__new__出来的空对象
# 修改对象的属性名
new_dic = {}
for k in dog_obj.__dict__:
new_dic[f'New_{k}'] = dog_obj.__dict__[k]
dog_obj.__dict__ = new_dic
return dog_obj
class Dog(metaclass=MyType):
"""
test
"""
def __init__(self, name, color):
self.name = name
self.color = color
def info(self):
print('name: {}, color: {}'.format(self.name, self.color))
def __call__(self, *args, **kwargs):
print("Dog.__call__")
return "wang"
def __new__(cls, *args, **kwargs):
obj = super().__new__(cls) # 使用父类,即object中的__new__方法来生成一个空对象
return obj
if __name__ == '__main__':
dog1 = Dog('ha', 'white') # MyType.__new__
print(dog1()) # Dog.__call__, wang, 在调用对象时触发类的__call__方法
print(dog1.__dict__) # {'New_name': 'ha', 'New_color': 'white'}