Python魔法函数之__new__
文中的演示代码基于CPython/Python 3.9
阅读Python 3 collections的namedtuple源码时,被__new__弄得很糊涂,翻看了官方教程和源码,藉此梳理
引言
如代码1所示,定义一个User类,并实例化两个对象
1 | |
执行结果如下:
1 | |
Python的解释器在执行时,new和init的第一个参数有区别
- 遇到
new魔法函数时,第一个参数传入的是类本身class - 遇到
init魔法函数时,第一个参数传入的是类的实例化对象instance
cls和self的命名是一种约定习惯,并非强制规范,这么命名避免混淆
student = User( "One")语句依次调用了这两个魔法函数,new调用在init之前,new方法用于实例化对象,init方法用于初始化对象。new方法的作用,是控制类的实例化过程
1 | |
metaclass关键字
如代码2 所示,引入metaclass关键字
1 | |
执行结果如下:
1 | |
在解释 代码2 结果之前,必须先强调一个重要的概念——一切皆对象,这个一切不限于:
- 函数 function
- 模板 module
- 变量 variable
- 实例 instance
- 类 class
没错,类也是对象
两个类
python中有两个重要的类:
object:python的始祖类,是继承树的根节点,都由object类派生或者迭代派生而来type: python的模板类,python中的任何对象,都由type类实例化或者迭代实例化而来
object类和type类他俩啥关系?他们同样适用这两个规则,object是type的实例化对象,object同时又是type的基类。
因此issubclass( type, object)和isinstance( object, type)这两条语句的返回值都是True
举一个具体一点的例子:
字符串"abc"它是str类的实例化对象,而str类本身,是type类的实例化对象。因此"abc".__class__.__class__的值为type,代码1中的User类,它当然也是type类的实例化对象,student.__class__.__class__的值也为type
如图所示,是字符串“abc”,字符串类string、object类和type类之间的关系:
metaclass
代码1 中的类定义语句,缺省条件下,默认继承object,元类是type
1 | |
回到代码2,Newuser引入了metaclass = UserMetaClass语句,将自己的元类设成了UserMetaClass,也就是说NewUser被视为UserMetaClass的实例化对象。
元类需要继承
type
在Python解释器视角下,NewUser的定义过程,即是UserMetaClass类实例化的过程。上一章我们说过,new方法用于控制类的实例化过程,因此定义NewUser时,会调用UserMetaClass的new方法
1 | |
这也是代码2 会打印信息的原因。代码2 执行过程,NewUser类自身的new方法没有被调用过,因为NewUser没有实例化对象。
尾记
本篇博客着重强调了两个概念:
- new魔法函数用于控制类的实例化过程
- 类也是对象
代码1 中的的new方法执行了两次,因为User类实例化了两次;而代码2 中的new方法只执行了一次,因为UserMetaClass只实例化了一次(定义NewUser类)。很多博客会介绍new方法,如果不强调类也是对象,我们很容易将类自身的new方法和元类的new方法混淆。
笔者将在下一篇博客中探讨:
- [ ]
type类 - [ ]
metaclass的传参过程
