元类究竟是什么东东?
前言
最近在看Python的面向对象编程,卡在了元类这个知识点,经过各种资料查询和学习,就有了这篇文章,还是那句话,能力时间有限,假如有错误,还望批评指正,谢谢。
元类概念
其实元类的概念很简单。
生成类的类就是元类。
我们都知道,对象是由类实例化来的,如下。
class Foo: passfoo = Foo()print(foo)<__main__.Foo object at 0x7fd9280ef250>
那我们思考下类又是谁产生的了,这时候我们就借助type函数,一看到底。
class Foo: passfoo = 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 = 1def test(self): print('test')type('Foo', (), {'i': i, 'test': test})
自己设置元类
我们也可以自己设置元类,我们需要做的就是在自己设置元类中继承type类,而后在生成的类中用metaclass指定自己设置元类。
class MyType(type): passclass Foo(metaclass=MyType): passprint(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 newfoo 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.__instanceclass Foo(metaclass=MyType): passfoo1 = Foo()foo2 = Foo()print(id(foo1), id(foo2))initcallcall140402884588256 140402884588256
今天的分享就到这了,我们下期再见~
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 元类究竟是什么东东?