元类到底是什么东东?
前言
最近在看Python的面向对象编程,卡在了元类这个知识点,经过各种资料查询和学习,就有了这篇文章,还是那句话,能力时间有限,如果有错误,还望批评指正,谢谢。
元类概念
其实元类的概念很简单。
生成类的类就是元类。
我们都知道,对象是由类实例化来的,如下。
class Foo:
pass
foo = Foo()
print(foo)
<__main__.Foo object at 0x7fd9280ef250>
那我们思考下类又是谁产生的了,这时候我们就借助type函数,一看究竟。
class Foo:
pass
foo = Foo()
print(type(foo))
print(type(Foo))
<class '__main__.Foo'>
<class 'type'>
我们发现Foo类的类型是type,我们再来看看python自带的数据类型是谁产生的。
print(type(int))
print(type(str))
<class 'type'>
<class 'type'>
我们可以发现,也是type,所以type就是元类。type元类实例化类(类对象),类再实例化对象,所以说Python万物皆对象,不管是用户自定义的类还是Python自带的类都是有type实例化来的。
type创建类
我们通常定义类都是使用class关键字。
class Foo:
i = 1
def test(self):
print('test')
元类是实例化类的,那type元类就应该可以直接实例化类,其语法为:
type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
i = 1
def test(self):
print('test')
type('Foo', (), {'i': i, 'test': test})
自定义元类
我们也可以自定义元类,我们需要做的就是在自定义元类中继承type类,然后在生成的类中用metaclass指定自定义元类。
class MyType(type):
pass
class Foo(metaclass=MyType):
pass
print(type(Foo))
<class '__main__.MyType'>
自定义元类的作用就是可以实现一些功能,应用于实例化类中,这样可以使实例化的类都具有相同的功能。在正式写一个自定义元类前,有必要讲解__new__、__init__和__call__魔术方法,只有了解和使用这些方法,才能让自定义元类实现我们想要的功能。
__new__魔术方法
介绍这些魔术方法的时候,我都会从普通类和自定义元类两个不同角度出发来讲解。
首先在普通类中,new魔术方法是构造方法,用于实例化对象。
class Foo:
a = 1
def __new__(cls, *args, **kwargs):
print(cls)
print(args)
print(kwargs)
print(object.__new__(cls))
return object.__new__(cls)
def test(self):
print('test')
foo = Foo()
<class '__main__.Foo'>
()
{}
<__main__.Foo object at 0x7f96380db160>
- 触发时间:实例化对象时
- 作用:实例化对象
- 参数:cls为当前类,args,kwargs为初始化的参数。
- 返回值:实例化的对象
在自定义元类中,new魔术方法就是来构造类(类对象)的了,其参数和type函数构造类是一样的。
class MyType(type):
def __new__(mcs, name, bases, dicts):
print(mcs)
print(name)
print(bases)
print(dicts)
print(super().__new__(mcs, name, bases, dicts))
return super().__new__(mcs, name, bases, dicts)
class Foo(metaclass=MyType):
a = 1
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
def test(self):
print('test')
foo = Foo()
<class '__main__.MyType'>
Foo
()
{'__module__': '__main__', '__qualname__': 'Foo', 'a': 1, '__new__': <function Foo.__new__ at 0x7fca60176790>, 'test': <function Foo.test at 0x7fca60176820>}
<class '__main__.Foo'>
- 触发时间:实例化类时。
- 作用:实例化类。
- 参数:mcs为当前元类,name为实例化类名,bases为实例化类的继承类名,dicts为实例化类的属性和方法。
- 返回值:实例化的类。
通过new魔术方法其实就可以实现很多自定义的功能了,例如我想让实例化的类的属性都变成大写。
class MyType(type):
def __new__(mcs, name, bases, dicts):
attrs = ((name, value) for name, value in dicts.items() if not name.startswith('__'))
dicts = dict((name.upper(), value) for name, value in attrs)
return super().__new__(mcs, name, bases, dicts)
class Foo(metaclass=MyType):
a = 1
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
def test(self):
print('test')
print(Foo.__dict__)
{'A': 1, 'TEST': <function Foo.test at 0x7fb0580b6820>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
__init__魔术方法
在普通类中,就是初始化方法,self为类实例化的对象,这个很好理解,毕竟这个函数我们接触的很多。
class Foo:
a = 1
def __init__(self, name):
self.name = name
def test(self):
print('test')
foo = Foo('li')
print(foo.name)
li
在自定义元类中,那就是初始化类对象。
class MyType(type):
def __new__(mcs, name, bases, dicts):
return super().__new__(mcs, name, bases, dicts)
def __init__(cls, name, bases, dicts):
print(cls)
super().__init__(name, bases, dicts)
class Foo(metaclass=MyType):
a = 1
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
def __init__(self, name):
self.name = name
def test(self):
print('test')
<class '__main__.Foo'>
__call__魔术方法
在普通类中,call方法会在实例化对象加括号时触发。
class Foo:
def __init__(self, name):
self.name = name
def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
foo = Foo('li')
foo()
<__main__.Foo object at 0x7fbd2806f250>
()
{}
在自定义元类中,我们知道类是由元类生成,那类加括号就会触发call方法。
Foo = MyType()
Foo() 相当于 MyType()()
class MyType(type):
def __new__(mcs, name, bases, dicts):
return super().__new__(mcs, name, bases, dicts)
def __init__(cls, name, bases, dicts):
print(cls)
super().__init__(name, bases, dicts)
def __call__(cls, *args, **kwargs):
# obj = cls.__new__(cls)
# cls.__init__(obj, *args, **kwargs)
return type.__call__(cls, *args, **kwargs)
class Foo(metaclass=MyType):
a = 1
def __new__(cls, *args, **kwargs):
print('foo new')
return object.__new__(cls)
def __init__(self, name):
print('foo init')
self.name = name
def test(self):
print('test')
foo = Foo('li')
print(foo.__dict__)
<class '__main__.Foo'>
foo new
foo init
{'name': 'li'}
我们可以看出,调用type的call函数,其实和手动调动类的new和init方法是一样的结果。
单例模式
最后,我们来用元类实现单例模式。单例模式就是实例化的对象只有一个,简单的说,就是如果被实例化过了就返回该实例,这样就只会有一个实例。
class MyType(type):
def __init__(cls, name, bases, dicts):
print('init')
cls.__instance = None
super().__init__(name, bases, dicts)
def __call__(cls, *args, **kwargs):
print('call')
if cls.__instance is None:
cls.__instance = type.__call__(cls, *args, **kwargs)
return cls.__instance
class Foo(metaclass=MyType):
pass
foo1 = Foo()
foo2 = Foo()
print(id(foo1), id(foo2))
init
call
call
140402884588256 140402884588256
今天的分享就到这了,我们下期再见~
转载自:https://juejin.cn/post/7079362278436372487