楔子 Python除了给我提供了很多的类之外,还支持我们定义属于自己的类,那么Python底层是如何做的呢?我们下面就来看看。
自定义class 老规矩,如果想知道底层是怎么做的,那么就必须要通过观察字节码来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Girl : name = "夏色祭" def __init__ (self ): print ("__init__" ) def f (self ): print ("f" ) def g (self, name ): self.name = name print (self.name) girl = Girl() girl.f() girl.g("神乐mea" ) """ __init__ f 神乐mea """
通过之前对函数机制的分析中,我们知道对于一个包含函数定义的Python源文件,在编译之后会得到一个和源文件对应的PyCodeObject对象,其内部的常量池中存储了函数编译之后的PyCodeObject对象。那么对于包含类的Python源文件,编译之后的结果又是怎么样的呢?
显然我们可以照葫芦画瓢,根据以前的经验我们可以猜测模块对应的PyCodeObject对象的常量池中肯定存储了类对应的PyCodeObject对象,类对应的PyCodeObject对象的常量池中则存储了__init__、f、g三个函数对应的PyCodeObject对象。然而事实也确实如此。
在介绍函数的时候,我们看到函数的声明(def语句)和函数的实现代码虽然是一个逻辑整体,但是它们的字节码指令却是分离在两个PyCodeObject对象中的。在类中,同样存在这样的分离现象。声明类的class语句,编译后的字节码指令存储在模块对应的PyCodeObject中,而类的实现、也就是类里面的逻辑,编译后的字节码指令序列则存储在类对应的的PyCodeObject中。所以我们在模块级别中只能找到类,无法直接找到类里面的成员。
另外还可以看到,类的成员函数和一般的函数相同,也会有这种声明和实现分离的现象。其实也很好理解,就把类和函数想象成变量就行了,类名、函数名就是变量名,而类、函数里面的逻辑想象成值,一个变量对应一个值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 s = """class Girl: name = "夏色祭" def __init__(self): print("__init__") def f(self): print("f") def g(self, name): self.name = name print(self.name)""" code = compile (s, "class" , "exec" ) print (code) print (code.co_consts[0 ]) print (code.co_consts[0 ].co_consts[6 ]) print (code.co_consts[0 ].co_consts[6 ].co_varnames)
class对象的动态元信息 class对象(class关键字创建的类)的元信息指的就是关于class的信息,比如说class的名称、它所拥有的的属性、方法,该class实例化时要为实例对象申请的内存空间大小等。对于模块中定义的class Girl来说,我们必须知道相应的信息:比如在class Girl中,有一个符号f,这个f对应一个函数;还有一个符号g,这个g也对应了一个函数。有了这些元信息,才能创建class对象,否则我们是没办法创建的。元信息是一个非常重要的概念,比如说Hive,数据的元信息就是存储在MySQL里面的,而在编程语言中,正是通过元信息才实现了反射等动态特性。而在Python中,元信息的概念被发挥的淋漓尽致,因此Python也提供了其他编程语言所不具备的高度灵活的动态特征。
老规矩,下面还是看一下字节码:
1 2 3 4 5 6 7 8 9 10 11 12 class Girl : name = "夏色祭" def __init__ (self ): print ("__init__" ) def f (self ): print ("f" ) def g (self, name ): self.name = name print (self.name)
这里我们先不涉及调用,只看类的创建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 1 0 LOAD_BUILD_CLASS 2 LOAD_CONST 0 (<code object Girl at 0x0000026FB0B3ABE0 , file "class" , line 1 >) 4 LOAD_CONST 1 ('Girl' ) 6 MAKE_FUNCTION 0 8 LOAD_CONST 1 ('Girl' ) 10 CALL_FUNCTION 2 12 STORE_NAME 0 (Girl) 14 LOAD_CONST 2 (None) 16 RETURN_VALUE Disassembly of <code object Girl at 0x0000026FB0B3ABE0 , file "class" , line 1 >: 1 0 LOAD_NAME 0 (__name__) 2 STORE_NAME 1 (__module__) 4 LOAD_CONST 0 ('Girl' ) 6 STORE_NAME 2 (__qualname__) 3 8 LOAD_CONST 1 ('夏色祭' ) 10 STORE_NAME 3 (name) 4 12 LOAD_CONST 2 (<code object __init__ at 0x0000026FB0961450 , file "class" , line 4 >) 14 LOAD_CONST 3 ('Girl.__init__' ) 16 MAKE_FUNCTION 0 18 STORE_NAME 4 (__init__) 7 20 LOAD_CONST 4 (<code object f at 0x0000026FB095AB30 , file "class" , line 7 >) 22 LOAD_CONST 5 ('Girl.f' ) 24 MAKE_FUNCTION 0 26 STORE_NAME 5 (f) 10 28 LOAD_CONST 6 (<code object g at 0x0000026FB0B472F0 , file "class" , line 10 >) 30 LOAD_CONST 7 ('Girl.g' ) 32 MAKE_FUNCTION 0 34 STORE_NAME 6 (g) 36 LOAD_CONST 8 (None) 38 RETURN_VALUE Disassembly of <code object __init__ at 0x0000026FB0961450 , file "class" , line 4 >: 5 0 LOAD_GLOBAL 0 (print) 2 LOAD_CONST 1 ('__init__' ) 4 CALL_FUNCTION 1 6 POP_TOP 8 LOAD_CONST 0 (None) 10 RETURN_VALUE Disassembly of <code object f at 0x0000026FB095AB30 , file "class" , line 7 >: 8 0 LOAD_GLOBAL 0 (print) 2 LOAD_CONST 1 ('f' ) 4 CALL_FUNCTION 1 6 POP_TOP 8 LOAD_CONST 0 (None) 10 RETURN_VALUE Disassembly of <code object g at 0x0000026FB0B472F0 , file "class" , line 10 >: 11 0 LOAD_FAST 1 (name) 2 LOAD_FAST 0 (self) 4 STORE_ATTR 0 (name) 12 6 LOAD_GLOBAL 1 (print) 8 LOAD_FAST 0 (self) 10 LOAD_ATTR 0 (name) 12 CALL_FUNCTION 1 14 POP_TOP 16 LOAD_CONST 0 (None) 18 RETURN_VALUE
字节码比较长,我们逐行分析,当然很多字节码我们都见过了,因此有的字节码介绍的时候就不会特别详细了。我们仔细观察一下字节码,会发现分为五个部分:模块的字节码、class Girl的字节码、class的三个函数的字节码。
我们先来看看模块的字节码 :
1 2 3 4 5 6 7 8 9 1 0 LOAD_BUILD_CLASS 2 LOAD_CONST 0 (<code object Girl at 0x0000026FB0B3ABE0 , file "class" , line 1 >) 4 LOAD_CONST 1 ('Girl' ) 6 MAKE_FUNCTION 0 8 LOAD_CONST 1 ('Girl' ) 10 CALL_FUNCTION 2 12 STORE_NAME 0 (Girl) 14 LOAD_CONST 2 (None) 16 RETURN_VALUE
0 LOAD_BUILD_CLASS: 我们注意到这又是一条我们没见过的新指令,从名字也能看出来这是要构建一个类;
2 LOAD_CONST: 加载Girl对应的PyCodeObject对象;
4 LOAD_CONST: 加载字符串"Girl"
6 MAKE_FUNCTION: 问题来了, 我们看到出现了MAKE_FUNCTION, 不是说要构建类吗? 为什么是MAKE_FUNCTION呢? 别急, 往下看;
8 LOAD_CONST: 再次加载字符串"Girl";
10 CALL_FUNCTION: 你看到了什么?函数调用?是的, 这个CALL_FUNCTION是用来构建类的, 至于怎么构建我们后面会说;
12 STORE_NAME: 将上一步构建好的类使用符号Girl保存;
我们看一下LOAD_BUILD_CLASS这个指令都干了哪些事情吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 case TARGET (LOAD_BUILD_CLASS) : { _Py_IDENTIFIER(__build_class__); PyObject *bc; if (PyDict_CheckExact(f->f_builtins)) { bc = _PyDict_GetItemIdWithError(f->f_builtins, &PyId___build_class__); if (bc == NULL ) { if (!_PyErr_Occurred(tstate)) { _PyErr_SetString(tstate, PyExc_NameError, "__build_class__ not found" ); } goto error; } Py_INCREF(bc); } else { PyObject *build_class_str = _PyUnicode_FromId(&PyId___build_class__); if (build_class_str == NULL ) goto error; bc = PyObject_GetItem(f->f_builtins, build_class_str); if (bc == NULL ) { if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) _PyErr_SetString(tstate, PyExc_NameError, "__build_class__ not found" ); goto error; } } PUSH(bc); DISPATCH(); }
LOAD_BUILD_CLASS做的事情很简单,就是从Python的内置函数中取得__build_class__将其入栈,然后下面的几个指令很好理解,但是却出现了一个CALL_FUNCTION,显然它是调用__build_class__创建类的。我们看到它的参数个数是2个,这两个参数分别是:A的PyFunctionObject、字符串”A”,因此:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class A (object ): pass A = __build_class__(<PyFunctionObject A>, "A" ) class A (int ): pass A = __build_class__(<PyFunctionObject A>, "A" , int )
我们实际操作一下:
1 2 3 4 5 6 7 8 9 10 11 import builtinsc = builtins.__build_class__(lambda : None , "MyInt" , int ) print (c.__name__) print (c.__base__) print (c(3 ) * c(5 ))
如果参数类型不正确的话,就会报出如下错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import sysimport builtinstry : builtins.__build_class__() except Exception as e: exc_type, exc_value, _ = sys.exc_info() print (exc_type, exc_value) try : builtins.__build_class__("" , "" ) except Exception as e: exc_type, exc_value, _ = sys.exc_info() print (exc_type, exc_value) try : builtins.__build_class__(lambda : 123 , 123 ) except Exception as e: exc_type, exc_value, _ = sys.exc_info() print (exc_type, exc_value)
记住这几个报错信息,后面马上就会看到。此外我们也看到,这个函数的一个参数叫func、第二个参数叫name。
所以现在就明白为什么会出现CALL_FUNCTION这条指令,__build_class__就是用来将一个函数对象变成一个class对象。
class对象的字节码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 1 0 LOAD_NAME 0 (__name__) 2 STORE_NAME 1 (__module__) 4 LOAD_CONST 0 ('Girl' ) 6 STORE_NAME 2 (__qualname__) 3 8 LOAD_CONST 1 ('夏色祭' ) 10 STORE_NAME 3 (name) 4 12 LOAD_CONST 2 (<code object __init__ at 0x0000026FB0961450 , file "class" , line 4 >) 14 LOAD_CONST 3 ('Girl.__init__' ) 16 MAKE_FUNCTION 0 18 STORE_NAME 4 (__init__) 7 20 LOAD_CONST 4 (<code object f at 0x0000026FB095AB30 , file "class" , line 7 >) 22 LOAD_CONST 5 ('Girl.f' ) 24 MAKE_FUNCTION 0 26 STORE_NAME 5 (f) 10 28 LOAD_CONST 6 (<code object g at 0x0000026FB0B472F0 , file "class" , line 10 >) 30 LOAD_CONST 7 ('Girl.g' ) 32 MAKE_FUNCTION 0 34 STORE_NAME 6 (g) 36 LOAD_CONST 8 (None) 38 RETURN_VALUE
对于一个类而言,调用其__module__属性,可以获取所在的模块。所以开始的LOAD_NAME和STORE_NAME是将符号__module__和全局命名空间中符号__name__的值关联了起来,并放入到该类的local名字空间中。
需要说明的是,我们在介绍函数的时候提过,当时我们说:”函数的局部变量是不可变的,在编译的时候就已经确定了,是以一种静态方式放在了运行时栈前面的那段内存中,并没有放在f_locals中,f_locals其实是一个NULL,我们通过locals()拿到的只是对运行时栈前面的内存的一个拷贝,函数里面的局部变量是通过静态方式来访问的”。但是类则不一样,类是可以动态修改的,可以随时增加属性、方法,这就意味着类是不可能通过静态方式来查找属性的。而事实上也确实如此,类也有一个f_locals,但它指向的就不再是NULL了,而和f_globals一样,也是一个PyDictObject对象。然后是LOAD_CONST,将字符串”Girl”加载进来,和__qualname__组成一个entry存储在Girl的local空间中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Girl : name = "夏色祭" def __init__ (self ): print ("__init__" ) def f (self ): print ("f" ) def g (self, name ): self.name = name print (self.name) print (__name__) print (Girl.__module__) print (Girl.__qualname__)
所以整体过程就是:先将PyCodeObject构建成函数,再通过__build_class__将函数变成一个类,当然__build_class__结束之后我们的Girl这个类就横空出世了。
因此剩下的来问题就是__build_class__是如何将一个函数变成类的,想要知道答案,那么只能去源码中一探究竟了。不过在看源码之前,我们还需要了解一样东西:metaclass。
元类,被誉为是深度的魔法,但是个人觉得有点夸张了。首先元类是做什么的,它是用来控制我们类的生成过程的,默认情况下,我们自定义的类都是由type创建的。但是我们可以手动指定某个类的元类,但是在介绍元类之前,我们还需要看一下Python中的两个特殊的魔法方法:__new__和__init__。
new__和__init 类在实例化的时候会自动调用__init__,但其实在调用__init__之前会先调用__new__。
__new__: 为实例对象申请一片内存;
__init__: 为实例对象设置属性;
1 2 3 4 5 6 7 8 9 10 class A : def __new__ (cls, *args, **kwargs ): print ("__new__" ) def __init__ (self ): print ("__init__" ) A()
然而我们看到只有__new__被调用了,__init__则没有。原因就在于__new__中必须将A的实例对象返回,才会执行__init__,并且执行的时候会自动将__new__的返回值作为参数传给self。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class A : def __new__ (cls, *args, **kwargs ): print ("__new__" ) return object .__new__(cls) def __init__ (self ): print ("__init__" ) A() """ __new__ __init__ """
所以一个对象是什么,取决于其类型对象的__new__返回了什么。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class A : def __new__ (cls, *args, **kwargs ): print ("__new__" ) return 123 def __init__ (self ): print ("__init__" ) a = A() print (a + 1 ) """ __new__ 124 """
我们看到A在实例化之后得到的是一个整型,原因就是__new__返回了123。最后一个就是参数问题,首先我们说__new__是创建实例对象的,__init__是为实例对象绑定属性的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class A : def __new__ (cls, name, age ): return object .__new__(cls) def __init__ (self, name, age ): self.name = name self.age = age a = A("夏色祭" , -1 )
创建类的另一种方式 创建类的时候可以使用class关键字创建,除了class关键字之外,我们还可以使用type这个古老却又强大的类来创建。
1 2 3 4 5 6 7 try : A = type ("A" , "" ) except Exception as e: print (e)
告诉我们type要么接收一个参数,要么接收三个参数。显然接收一个参数查看类型不需要再说了,我们看看怎么用来用type创建一个类。
1 2 3 4 5 6 7 8 9 10 class A (list ): name = "夏色祭" val = type ("A" , (list , ), {"name" : "夏色祭" }) print (val) print (val.__name__) print (val.__base__) print (val.name)
所以还是很简单的,我们还可以自定义一个类继承自type。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class MyType (type ): def __new__ (mcs, name, bases, attr ): print (name) print (bases) print (attr) class A (int , object , metaclass=MyType): name = "夏色祭" """ A (<class 'int'>, <class 'object'>) {'__module__': '__main__', '__qualname__': 'A', 'name': '夏色祭'} """ print (A) """ 我们说__new__一定要将创建的实例对象返回才可以, 这里的MyType是元类 所以类对象A就等于MyType的实例对象, MyType的__new__就负责为类对象A分配空间 但是显然我们这里并没有分配, 而且返回的还是一个None, 如果我们返回的是123, 那么print(a)就是123 """
所以元类和类之间的关系 和 类与实例对象的关系,之间是很相似的,因为完全可以把类对象看成是元类的实例对象。因此A既然指定了metaclass为MyType,表示A这个类由MyType创建,那么MyType的__new__函数返回了什么,A就是什么。
1 2 3 4 5 6 7 8 9 10 11 class MyType (type ): def __new__ (mcs, name, bases, attr ): return "嘿嘿嘿" class A (metaclass=MyType): pass print (A + "哟哟哟" )
这便是Python语言具备的高度动态特性,那么问题来了,如果我想把A创建出来、像普通的类一样使用的话,该咋办呢?因为默认情况下是由type创建,底层帮你做好了,但是现在是我们手动指定元类,那么一切就需要我们来手动指定了。显然,这里创建还是要依赖于type,只不过需要我们手动指定,而且在手动指定的同时我们还可以增加一些我们自己的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class MyType (type ): def __new__ (mcs, name, bases, attr ): name = name * 2 bases = (list ,) attr.update({"name" : "神乐mea" , "nickname" : "屑女仆" }) return super ().__new__(mcs, name, bases, attr) class Girl (metaclass=MyType): pass print (Girl.__name__) print (Girl("你好呀" )) print (Girl.name, Girl.nickname)
我们之前还说过,一个类在没有指定的metaclass的时候,如果它的父类指定了,那么这个类的metaclass等于父类的metaclass。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class MyType (type ): def __new__ (mcs, name, bases, attr ): name = name * 2 bases = (list ,) attr.update({"name" : "神乐mea" , "nickname" : "屑女仆" }) return super ().__new__(mcs, name, bases, attr) class Girl (metaclass=MyType): pass class A (Girl ): pass print (A.__class__) print (A.__name__)
我们之前还举了个flask的例子,一种更加优雅的写法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class MyType (type ): def __new__ (mcs, name, bases, attr ): return super ().__new__(mcs, name, bases, attr) def with_metaclass (meta, bases ): return meta("tmp" , bases, {"gender" : "female" }) class Girl (with_metaclass(MyType, (list ,))): pass print (Girl.__class__) print (Girl.__bases__) print (Girl.__mro__) print (Girl.gender)
注意:我们说创建类的对象是元类,元类要么是type、要么是继承自type的子类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class MyType (type ): def __new__ (mcs, name, bases, attr ): return super ().__new__(mcs, name, bases, attr) Girl = type .__new__(MyType, "GirlGirlGirl" , (list ,), {"foo" : lambda self, value: value + 123 }) print (Girl.__name__) g = Girl() print (g.foo(123 )) try : type .__new__(int , "A" , (object ,), {}) except TypeError as e: print (e)
怎么样,是不是觉得元类很简单呢?其实元类没有什么复杂的。
再举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class MyType (type ): def __new__ (mcs, name, bases, attr ): if "f" in attr: attr.pop("f" ) return super ().__new__(mcs, name, bases, attr) class Girl (metaclass=MyType): def f (self ): return "f" def g (self ): return "g" print (Girl().g()) try : print (Girl().f()) except AttributeError as e: print (e) """ 惊了, 我们看到居然没有f这个属性, 我们明显定义了啊, 原因就是我们在创建类的时候将其pop掉了 首先创建一个类需要三个元素: 类名、继承的基类、类的一些属性(以字典的形式, 属性名: 属性值) 然后会将这三个元素交给元类进行创建, 但是我们在创建的时候偷偷地将f从attr里面给pop掉了 因此创建出来的类是没有f这个函数的 """
元类确实蛮有趣的,而且也没有想象中的那么难,可以多了解一下。
特殊的魔法函数 此外我们再来看两个和元类有关的魔法函数:
prepared
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class MyType (type ): @classmethod def __prepare__ (mcs, name, bases ): print ("__prepared__" ) return {} def __new__ (mcs, name, bases, attr ): print ("__new__" ) return super ().__new__(mcs, name, bases, attr) class Girl (metaclass=MyType): pass """ __prepared__ __new__ """
我们看到__prepare__会在__new__方法之前被调用,那么它是做什么的呢?答案是添加属性的,我们解释一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class MyType (type ): @classmethod def __prepare__ (mcs, name, bases ): return {"name" : "夏色祭" } def __new__ (mcs, name, bases, attr ): return super ().__new__(mcs, name, bases, attr) class Girl (metaclass=MyType): def f (self ): return "f" def g (self ): return "g" print (Girl.name)
此外__prepared__这个方法是被classmethod装饰的,另外里面一定要返回一个mapping,否则报错:TypeError: MyType.prepare () must return a mapping, not xxx
init_subclass
它类似于一个钩子函数,在一些简单地场景下可以代替元类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Base : def __init_subclass__ (cls, **kwargs ): print (cls, kwargs) class A (Base ): pass """ <class '__main__.A'> {} """ class B (Base, name="夏色祭" , age=-1 ): pass """ <class '__main__.B'> {'name': '夏色祭', 'age': -1} """
所以父类的__init_subclass__里面的cls并不是父类本身,而是继承它的类。kwargs,就是额外设置的一些属性。因此我们可以实现一个属性添加器。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Base : def __init_subclass__ (cls, **kwargs ): for k, v in kwargs.items(): setattr (cls, k, v) class A (Base, name="夏色祭" , age=-1 , __str__=lambda self: "__str__" ): pass print (A.name, A.age) print (A())
除了属性添加器,我们还可以实现一个属性拦截器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Base : def __init_subclass__ (cls, **kwargs ): if hasattr (cls, "yoyoyo" ) and hasattr (cls.yoyoyo, "__code__" ): raise Exception(f"{cls.__name__} 不允许定义'yoyoyo'函数" ) class A (Base ): yoyoyo = 123 try : class B (Base ): def yoyoyo (self ): pass except Exception as e: print (e)
有了这些元类相关的知识,我们后面在分析源码的时候就会轻松一些。
我们说LOAD_BUILD_CLASS是将一个PyFunctionObject变成一个类,尽管它写在最前面,但实际上是需要将class A对应的PyCodeObject对象包装成一个PyFunctionObject对象之后才能执行。我们说__build_class__是用来将PyFunctionObject变成类的函数,我们来看看它长什么样子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 static PyMethodDef builtin_methods[] = { {"__build_class__" , (PyCFunction)(void (*)(void ))builtin___build_class__, METH_FASTCALL | METH_KEYWORDS, build_class_doc}, } static PyObject *builtin___build_class__(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *func, *name, *bases, *mkw, *meta, *winner, *prep, *ns, *orig_bases; PyObject *cls = NULL , *cell = NULL ; int isclass = 0 ; if (nargs < 2 ) { PyErr_SetString(PyExc_TypeError, "__build_class__: not enough arguments" ); return NULL ; } func = args[0 ]; if (!PyFunction_Check(func)) { PyErr_SetString(PyExc_TypeError, "__build_class__: func must be a function" ); return NULL ; } name = args[1 ]; if (!PyUnicode_Check(name)) { PyErr_SetString(PyExc_TypeError, "__build_class__: name is not a string" ); return NULL ; } orig_bases = _PyTuple_FromArray(args + 2 , nargs - 2 ); if (orig_bases == NULL ) return NULL ; bases = update_bases(orig_bases, args + 2 , nargs - 2 ); if (bases == NULL ) { Py_DECREF(orig_bases); return NULL ; } if (kwnames == NULL ) { meta = NULL ; mkw = NULL ; } else { mkw = _PyStack_AsDict(args + nargs, kwnames); if (mkw == NULL ) { Py_DECREF(bases); return NULL ; } meta = _PyDict_GetItemIdWithError(mkw, &PyId_metaclass); if (meta != NULL ) { Py_INCREF(meta); if (_PyDict_DelItemId(mkw, &PyId_metaclass) < 0 ) { Py_DECREF(meta); Py_DECREF(mkw); Py_DECREF(bases); return NULL ; } isclass = PyType_Check(meta); } else if (PyErr_Occurred()) { Py_DECREF(mkw); Py_DECREF(bases); return NULL ; } } if (meta == NULL ) { if (PyTuple_GET_SIZE(bases) == 0 ) { meta = (PyObject *) (&PyType_Type); } else { PyObject *base0 = PyTuple_GET_ITEM(bases, 0 ); meta = (PyObject *) (base0->ob_type); } Py_INCREF(meta); isclass = 1 ; } if (isclass) { winner = (PyObject *)_PyType_CalculateMetaclass((PyTypeObject *)meta, bases); if (winner == NULL ) { Py_DECREF(meta); Py_XDECREF(mkw); Py_DECREF(bases); return NULL ; } if (winner != meta) { Py_DECREF(meta); meta = winner; Py_INCREF(meta); } } if (_PyObject_LookupAttrId(meta, &PyId___prepare__, &prep) < 0 ) { ns = NULL ; } else if (prep == NULL ) { ns = PyDict_New(); } else { PyObject *pargs[2 ] = {name, bases}; ns = _PyObject_FastCallDict(prep, pargs, 2 , mkw); Py_DECREF(prep); } if (ns == NULL ) { Py_DECREF(meta); Py_XDECREF(mkw); Py_DECREF(bases); return NULL ; } if (!PyMapping_Check(ns)) { PyErr_Format(PyExc_TypeError, "%.200s.__prepare__() must return a mapping, not %.200s" , isclass ? ((PyTypeObject *)meta)->tp_name : "<metaclass>" , Py_TYPE(ns)->tp_name); goto error; } }
可以看到,一个简单的类定义,Python底层究竟做了多少事情啊,不过显然这还没完。
我们前面说,Python虚拟机获得了关于class的属性表(动态元信息),比如所有的方法、属性,所以我们可以说,class的动态元信息包含了class的所有属性。但是对于这个class对象的类型是什么,应该如何创建、要分配多少内存,却没有任何的信息。而在builtin___build_class__
中,metaclass正是关于class对象的另一部分元信息,我们称之为静态元信息。在静态元信息中,隐藏着所有的类对象应该如何创建的信息,注意:是所有的类对象。
从源码中我们可以看到,如果用户指定了metaclass,那么会选择指定的metaclass,如果没有指定,那么会使用第一个继承的基类的__class__作为该class的metaclass。
对于PyLongObject、PyDictObject这些Python中的实例对象,所有的元信息存储在对应的类对象中(PyLong_Type,PyDict_Type)。但是对于类对象来说,其元信息的静态元信息存储在对应的元类(PyType_Type)中,动态元信息则存储在本身的local名字空间中。但是为什么这么做呢?为什么对于类对象来说,其元信息要游离成两部分呢?都存在metaclass里面不香吗?这是因为,用户在.py文件中可以定义不同的class,这个元信息必须、且只能是动态的,所以它是不适合保存在metaclass中的,因此类对象的创建策略等这些所有class都会共用的元信息,会存储在metaclass里面。
像Python的内建对象都是Python静态提供的,它们都具备相同的接口集合(底层都是PyTypeObject结构体实例),支持什么操作一开始就定义好了。只不过有的可以用,有的不能用。比如PyLongObject可以使用nb_add,但是PyDictObject不能。而PyDictObject可以使用mp_subscript,但是PyLongObject不可以。尽管如此,但这不影响它们的所有元信息都可以完全存储在类型对象中。但是用户自定义的class对象,接口是动态的,不可能在metaclass中静态指定。
既然创建了元类,那么下面显然就开始调用了。通过函数 *PyObject_Call* 调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 PyObject * PyObject_Call (PyObject *callable, PyObject *args, PyObject *kwargs) { else { call = callable->ob_type->tp_call; if (call == NULL ) { PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable" , callable->ob_type->tp_name); return NULL ; } } } static PyObject *type_call (PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *obj; if (type->tp_new == NULL ) { PyErr_Format(PyExc_TypeError, "cannot create '%.100s' instances" , type->tp_name); return NULL ; } obj = type->tp_new(type, args, kwds); obj = _Py_CheckFunctionResult((PyObject*)type, obj, NULL ); if (obj == NULL ) return NULL ; if (type == &PyType_Type && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 && (kwds == NULL || (PyDict_Check(kwds) && PyDict_GET_SIZE(kwds) == 0 ))) return obj; if (!PyType_IsSubtype(Py_TYPE(obj), type)) return obj; type = Py_TYPE(obj); if (type->tp_init != NULL ) { int res = type->tp_init(obj, args, kwds); if (res < 0 ) { assert(PyErr_Occurred()); Py_DECREF(obj); obj = NULL ; } else { assert(!PyErr_Occurred()); } } return obj; }
tp_new指向type_new,这个type_new是我们创建class对象的第一案发现场。我们看一下type_new的源码,位于 *Objects/typeobject.c* 中,这个函数的代码比较长,我们会有删减,像那些检测的代码我们就省略掉了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 static PyObject *type_new (PyTypeObject *metatype, PyObject *args, PyObject *kwds) { PyObject *name, *bases = NULL , *orig_dict, *dict = NULL ; PyObject *qualname, *slots = NULL , *tmp, *newslots, *cell; PyTypeObject *type = NULL , *base, *tmptype, *winner; PyHeapTypeObject *et; PyMemberDef *mp; Py_ssize_t i, nbases, nslots, slotoffset, name_size; int j, may_add_dict, may_add_weak, add_dict, add_weak; _Py_IDENTIFIER(__qualname__); _Py_IDENTIFIER(__slots__); _Py_IDENTIFIER(__classcell__); if (metatype == &PyType_Type) { const Py_ssize_t nargs = PyTuple_GET_SIZE(args); const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_GET_SIZE(kwds); if (nargs == 1 && nkwds == 0 ) { PyObject *x = PyTuple_GET_ITEM(args, 0 ); Py_INCREF(Py_TYPE(x)); return (PyObject *) Py_TYPE(x); } if (nargs != 3 ) { PyErr_SetString(PyExc_TypeError, "type() takes 1 or 3 arguments" ); return NULL ; } } if (!PyArg_ParseTuple(args, "UO!O!:type.__new__" , &name, &PyTuple_Type, &bases, &PyDict_Type, &orig_dict)) return NULL ; nbases = PyTuple_GET_SIZE(bases); if (nbases == 0 ) { base = &PyBaseObject_Type; bases = PyTuple_Pack(1 , base); if (bases == NULL ) return NULL ; nbases = 1 ; } else { _Py_IDENTIFIER(__mro_entries__); for (i = 0 ; i < nbases; i++) { tmp = PyTuple_GET_ITEM(bases, i); if (PyType_Check(tmp)) { continue ; } if (_PyObject_LookupAttrId(tmp, &PyId___mro_entries__, &tmp) < 0 ) { return NULL ; } if (tmp != NULL ) { PyErr_SetString(PyExc_TypeError, "type() doesn't support MRO entry resolution; " "use types.new_class()" ); Py_DECREF(tmp); return NULL ; } } winner = _PyType_CalculateMetaclass(metatype, bases); if (winner == NULL ) { return NULL ; } if (winner != metatype) { if (winner->tp_new != type_new) return winner->tp_new(winner, args, kwds); metatype = winner; } base = best_base(bases); if (base == NULL ) { return NULL ; } Py_INCREF(bases); } dict = PyDict_Copy(orig_dict); if (dict == NULL ) goto error; slots = _PyDict_GetItemIdWithError(dict, &PyId___slots__); nslots = 0 ; add_dict = 0 ; add_weak = 0 ; may_add_dict = base->tp_dictoffset == 0 ; may_add_weak = base->tp_weaklistoffset == 0 && base->tp_itemsize == 0 ; if (slots == NULL ) { } else { } type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots); if (type == NULL ) goto error; et = (PyHeapTypeObject *)type; Py_INCREF(name); et->ht_name = name; et->ht_slots = slots; slots = NULL ; type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_BASETYPE; if (base->tp_flags & Py_TPFLAGS_HAVE_GC) type->tp_flags |= Py_TPFLAGS_HAVE_GC; type->tp_as_async = &et->as_async; type->tp_as_number = &et->as_number; type->tp_as_sequence = &et->as_sequence; type->tp_as_mapping = &et->as_mapping; type->tp_as_buffer = &et->as_buffer; type->tp_name = PyUnicode_AsUTF8AndSize(name, &name_size); if (!type->tp_name) goto error; if (strlen (type->tp_name) != (size_t )name_size) { PyErr_SetString(PyExc_ValueError, "type name must not contain null characters" ); goto error; } type->tp_bases = bases; bases = NULL ; Py_INCREF(base); type->tp_base = base; Py_INCREF(dict); type->tp_dict = dict; if (_PyDict_GetItemIdWithError(dict, &PyId___module__) == NULL ) { if (PyErr_Occurred()) { goto error; } tmp = PyEval_GetGlobals(); if (tmp != NULL ) { tmp = _PyDict_GetItemIdWithError(tmp, &PyId___name__); if (tmp != NULL ) { if (_PyDict_SetItemId(dict, &PyId___module__, tmp) < 0 ) goto error; } else if (PyErr_Occurred()) { goto error; } } } qualname = _PyDict_GetItemIdWithError(dict, &PyId___qualname__); tmp = _PyDict_GetItemIdWithError(dict, &PyId___new__); if (tmp != NULL && PyFunction_Check(tmp)) { tmp = PyStaticMethod_New(tmp); if (tmp == NULL ) goto error; if (_PyDict_SetItemId(dict, &PyId___new__, tmp) < 0 ) { Py_DECREF(tmp); goto error; } Py_DECREF(tmp); } else if (tmp == NULL && PyErr_Occurred()) { goto error; } tmp = _PyDict_GetItemIdWithError(dict, &PyId___init_subclass__); if (tmp != NULL && PyFunction_Check(tmp)) { tmp = PyClassMethod_New(tmp); if (tmp == NULL ) goto error; if (_PyDict_SetItemId(dict, &PyId___init_subclass__, tmp) < 0 ) { Py_DECREF(tmp); goto error; } Py_DECREF(tmp); } else if (tmp == NULL && PyErr_Occurred()) { goto error; } tmp = _PyDict_GetItemIdWithError(dict, &PyId___class_getitem__); type->tp_basicsize = slotoffset; type->tp_itemsize = base->tp_itemsize; type->tp_members = PyHeapType_GET_MEMBERS(et); if (PyType_Ready(type) < 0 ) goto error; fixup_slot_dispatchers(type); if (type->tp_dictoffset) { et->ht_cached_keys = _PyDict_NewKeysForClass(); } if (set_names(type) < 0 ) goto error; if (init_subclass(type, kwds) < 0 ) goto error; Py_DECREF(dict); return (PyObject *)type; error: Py_XDECREF(dict); Py_XDECREF(bases); Py_XDECREF(slots); Py_XDECREF(type); return NULL ; }
Python虚拟机首先会将类名、基类列表和属性表从tuple对象中解析出来,然后会基于基类列表及传入的metaclass(参数metatype)确定最佳的metaclass和base。
随后,python虚拟机会调用metatype->tp_alloc
尝试为要创建的类对象分配内存。这里需要注意的是,在PyType_Type中,我们发现tp_alloc是一个NULL,这显然不正常。但是不要忘记,我们之前提到,在Python进行初始化时,会对所有的内建对象通过PyType_Ready进行初始化,在这个初始化过程中,有一项动作就是从基类继承各种操作。由于type.__bases__中的第一个基类是object,所以type会继承object中的tp_alloc操作,即 *PyType_GenericAlloc* 。对于我们的任意继承自object的class对象来说, *PyType_GenericAlloc* 将申请metatype->tp_basicsize + metatype->tp_itemsize
大小的内存空间。从PyType_Type的定义中我们看到,这个大小实际就是 *sizeof(PyHeapTypeObject) + sizeof(PyMemerDef)* 。因此在这里应该就明白了PyHeapTypeObject这个老铁到底是干嘛用的了,之前因为偏移量的问题,折腾了不少功夫,甚至让人觉得这有啥用啊,但是现在意识到了,这个老铁是为用户自定义class准备的。
接下来就是设置class对象的各个域,其中包括了在tp_dict上设置属性表,也就是__dict__。另外注意的是,这里还计算了类对象对应的实例对象所需要的内存大小信息,换言之,我们类创建一个实例对象时,需要为这个实例对象申请多大的内存空间呢?对于任意继承object的class对象来说,这个大小为PyBaseObject_Type->tp_basicsize + 16
。其中的16是2 * sizeof(PyObject *)。为什么后面要跟着两个PyObject *的空间,因为这些空间的地址被设置给了 *tp_dictoffset* 和 *tp_weaklistoffset* 了呢?这一点将在下一篇博客中进行解析,它是和实例对象的属性字典密切相关的。
最后,Python虚拟机还会调用PyType_Ready对class定义的类对象(这里简称class对象)
进行和内建对象一样的初始化动作,到此class对象才算正式创建完毕。那么内建对象和class对象在内存布局上面有什么区别呢?毕竟都是类对象。
本质上,无论用户自定义的class对象还是内建对象,在Python虚拟机内部,都可以用一个PyTypeObject来表示。但不同的是,内建对象的PyTypeObject以及与其关联的PyNumberMethods等属性的内存位置都是在编译时确定的,它们在内存中的位置是分离的。而用户自定义的class对象的PyTypeObject和PyNumberMethods等内存位置是连续的,必须在运行时动态分配内存。
现在我们算是对python中可调用(callable)这个概念有一个感性认识了,在python中可调用这个概念是一个相当通用的概念,不拘泥于对象、大小,只要类型对象定义了tp_call操作,就能进行调用操作。我们已经看到,python中的对象class对象是调用metaclass创建。那么显然,调用class对象就能得到实例对象。
小结 这一次我们介绍了自定义的类在底层是如何实现的,但是关于类的知识点还有很多,比如:魔法方法、描述符等等,我们可能还需要两到三篇来进行介绍。