1. 元类创建的过程
class Mymeta(type):
def __new__(cls, *args, **kwargs):
print('1.type 调用了 Mymeta 的 new 方法--生成一个空对象,即 People 类')
"这里调用的是 type 的 new 方法,传入参数需要注意全部传会 type 元类"
return super().__new__(cls, *args, **kwargs)
def __init__(self, class_name, class_bases, class_dic):
print('2.初始化这个对象--- People 类,给 People 类添加额外的功能')
super(Mymeta, self).__init__(class_name, class_bases, class_dic) # 重用父类的功能
# 自定义的类的功能
if not class_name.istitle():
raise TypeError('类名%s请修改为首字母大写' % class_name)
if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:
raise TypeError('类中必须有文档注释,并且文档注释不能为空')
# 传入 Mymeta的参数:People, 以及传入People的参数
def __call__(self, *args, **kwargs):
"""
self---<class '__main__.People'>
:param args: (1,)
:param kwargs: {'y': 2}
:return: 返回最终初始化好的代码
"""
print('3.调用了 Mymeta 的 call 方法')
# 调用 People 类里的 __new__方法,生成空对象
People_obj = self.__new__(self, *args, **kwargs)
# 调用 People 类里的 __init__方法,初始化空对象,注意:第一个传入的参数是生成好的空对象
self.__init__(People_obj, *args, **kwargs)
# 给 People 类生成的对象 obj 添加额外的功能
print("9.给 People 类生成的对象 obj 添加额外的功能")
People_obj.__dict__["新增一个属性"] = None
# 返回初始化好的对象
return People_obj
class People(metaclass=Mymeta):
"""People 类的注释"""
# 产生 People 类真正的对象
def __new__(cls, *args, **kwargs):
# 在这里就可以定制功能
print('4.生成 People 类的空对象')
print('5.传入的位置参数', args)
print('6.传入的位置参数', kwargs)
# 调用所继承的父类的__new__方法,这里就是 object 类,一定要传入 cls(当前这个类)
"这里要区别于自定义元类的 new 方法,自定义元类调用的是 type 的 new 方法,传入参数是不一样的"
return super().__new__(cls)
def __init__(self, x, y=None):
print("7.初始化 People 类的对象")
self.x = x
self.y = y
print("8.初始化 People 类的对象结束")
# 调用People 类生成对象---> People()= Mymeta.__call__()
obj = People(1, y=2)
print('10.最终的对象字典:', obj.__dict__)
image.png
元类创建过程
2. 元类的应用
- python原生的list无add方法,为了实现C#的add方式我们使用元类
class ListMeta(type):
def __new__(cls, name, bases, attrs):
# 在类属性当中添加了add函数
# 通过匿名函数映射到append函数上
attrs['add'] = lambda self, value: self.append(value)
return super().__new__(cls, name, bases, attrs)
class MyList(list, metaclass=ListMeta):
pass
lt = MyList()
lt.add(3)
lt.add(4)
print(lt)
>>>
[3, 4]
元类的使用场景:涉及类属性变更和类创建的时候
一个工厂模式的,我们要求所有的类,都是通过这个工厂创建,但是现在这种情况,可以跳过工厂来创建,工厂中可能有一些必须执行的方法,例如统计生产了多少产品, 没生产一个产品,发送一点短信
class Last_of_us:
def play(self):
print('the Last Of Us is really funny')
class Uncharted:
def play(self):
print('the Uncharted is really funny')
class PSGame:
def play(self):
print('PS has many games')
class GameFactory:
games = {'last_of_us': Last_of_us, 'uncharted': Uncharted}
def __new__(cls, name):
if name in cls.games:
return cls.games[name]()
else:
return PSGame()
uncharted = GameFactory('uncharted').play()
last_of_us = GameFactory('last_of_us').play()
Last_of_us().play()
>>>
the Uncharted is really funny
the Last Of Us is really funny
the Last Of Us is really funny
- 改进
class check_factory(type):
def __call__(self, *args, **kwargs):
if len(args) == 0 or args[0] != 'factory':
raise TypeError("Can't instantiate directly")
class Last_of_us(metaclass=check_factory):
def play(self):
print('the Last Of Us is really funny')
class Uncharted:
def play(self):
print('the Uncharted is really funny')
class PSGame:
def play(self):
print('PS has many games')
class GameFactory:
games = {'last_of_us': Last_of_us, 'uncharted': Uncharted}
def __new__(cls, name):
if name in cls.games:
return cls.games[name]()
else:
return PSGame()
uncharted = GameFactory('uncharted').play()
last_of_us = GameFactory('last_of_us')
Last_of_us
控制子类生成
假设你是个库的作者,你要求用户继承你的类必须实现特定的方法,比如下面这个:
# 库提供的父类
class Father():
def foo(self):
return self.bar()
# 用户写的子类
class Child(Father):
def bar(self):
return True
如果想强制让子类实现foo方法
class Meta(type):
def __new__(cls, name, bases, dct, **kwargs):
if name != 'Father' and 'foo' not in dct:
raise TypeError('Class must contain foo() method.')
return super().__new__(cls, name, bases, dct, **kwargs)
# 添加了元类
class Father(metaclass=Meta):
def foo(self):
return self.foo()
class Child(Father):
def bar(self):
return True
c1= Child()
>>>
TypeError: Class must contain foo() method.
动态给子类添加方法
比如说,我只想让名叫 Apple 的子类具有 sayHi() 方法:
class Meta(type):
def __new__(cls, name, bases, dct, **kwargs):
if name == 'Apple':
dct.update({
'sayHi': lambda: 'Hi I am Apple'
})
return super().__new__(cls, name, bases, dct, **kwargs)
class Food(metaclass=Meta):
pass
class Apple(Food):
pass
class Pear(Food):
pass
Apple.sayHi()
Pear.sayHi()
>>>
AttributeError: type object 'Pear' has no attribute 'sayHi'
除了判断类的名称外,你可以编写更加复杂的判据,来实现业务的要求。
网友评论