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
的传参过程