Python魔法函数之__new__

文中的演示代码基于CPython/Python 3.9

阅读Python 3 collectionsnamedtuple源码时,被__new__弄得很糊涂,翻看了官方教程和源码,藉此梳理

引言

代码1所示,定义一个User类,并实例化两个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 代码.1

class User:
def __new__( cls, *args, **kwargvs):
print( f"In {cls} new function")
return super().__new__( cls)

def __init__(self, name = "Juboge"):
self.name = name
print( f"In {self} init function, with name = {name}")

if __name__ == "__main__":
student = User( "One")
print()
student_two = User( "Two")

执行结果如下:

1
2
3
4
5
In <class '__main__.User'> new function
In <__main__.User object at 0x000001CD83CACBE0> init function, with name = One

In <class '__main__.User'> new function
In <__main__.User object at 0x000001CD83CACD30> init function, with name = Two

Python的解释器在执行时,newinit的第一个参数有区别

  • 遇到new魔法函数时,第一个参数传入的是类本身class
  • 遇到init魔法函数时,第一个参数传入的是类的实例化对象instance

clsself的命名是一种约定习惯,并非强制规范,这么命名避免混淆

student = User( "One")语句依次调用了这两个魔法函数,new调用在init之前,new方法用于实例化对象,init方法用于初始化对象。new方法的作用,是控制类的实例化过程

1
2
3
4
student = User( "One")
# 等价于
student = __new__( User, ( "One", ) , {} )
__init__( student, ( "One", ), {} )

metaclass关键字

代码2 所示,引入metaclass关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 代码.2

class UserMetaClass( type):
def __new__( cls, *args, **kwargs):
print( f"In {cls} new function")
return super().__new__( cls, *args, **kwargs )


class NewUser( metaclass = UserMetaClass):
def __new__( cls, *args, **kwargvs):
print( f"In {cls} new function")
return super().__new__( cls)

def __init__(self, name = "Juboge"):
print( f"In {self} new function")
self.name = name


if __name__ == '__main__':
# nstudent = NewUser( "One")
pass

执行结果如下:

1
In <class '__main__.UserMetaClass'> new function

在解释 代码2 结果之前,必须先强调一个重要的概念——一切皆对象,这个一切不限于:

  • 函数 function
  • 模板 module
  • 变量 variable
  • 实例 instance
  • 类 class

没错,类也是对象

两个类

python中有两个重要的类:

  • object:python的始祖类,是继承树的根节点,都由object类派生或者迭代派生而来
  • type: python的模板类,python中的任何对象,都由type类实例化或者迭代实例化而来

object类和type类他俩啥关系?他们同样适用这两个规则,objecttype的实例化对象,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”,字符串类stringobject类和type类之间的关系:

插图1

metaclass

代码1 中的类定义语句,缺省条件下,默认继承object,元类是type

1
2
3
4
5
class User:
pass
# 由于类的声明没有加其它关键字,等价于:
class User( object, metaclass = type):
pass

回到代码2,Newuser引入了metaclass = UserMetaClass语句,将自己的元类设成了UserMetaClass,也就是说NewUser被视为UserMetaClass的实例化对象。

元类需要继承type

在Python解释器视角下,NewUser的定义过程,即是UserMetaClass类实例化的过程。上一章我们说过,new方法用于控制类的实例化过程,因此定义NewUser时,会调用UserMetaClassnew方法

1
2
3
class NewUser( metaclass = UserMetaClass):
# 等价于
NewUser = UserMetaClass.__new__( UserMetaCLass, *args, **kwargs )

这也是代码2 会打印信息的原因。代码2 执行过程,NewUser类自身的new方法没有被调用过,因为NewUser没有实例化对象。

尾记

本篇博客着重强调了两个概念:

  1. new魔法函数用于控制类的实例化过程
  2. 类也是对象

代码1 中的的new方法执行了两次,因为User类实例化了两次;而代码2 中的new方法只执行了一次,因为UserMetaClass只实例化了一次(定义NewUser类)。很多博客会介绍new方法,如果不强调类也是对象,我们很容易将类自身的new方法和元类的new方法混淆。

笔者将在下一篇博客中探讨:

  • [ ] type
  • [ ] metaclass的传参过程

Python魔法函数之__new__
https://www.ydhuyong.online/2024/03/11/魔法函数/
作者
Yong
发布于
2024年3月11日
更新于
2024年3月13日
许可协议