12.4 类型类
在 Python 中,对象都有 ob_type
属性,你可以使用内置函数 type()
获取这个属性的值:
type()
的返回结果是 PyTypeObject
的一个实例:
类型对象用于定义抽象基类的实现。
例如,对象总会实现 __repr__()
方法:
在任何对象的类型定义中,__repr__()
的实现总是位于相同的地址。这个位置被称之为 类型槽。
类型槽
所有类型槽都定义在 Include/cpython/object.h
。
每个类型槽有属性名和函数签名。例如 __repr__()
函数的属性名为 tp_repr
,函数签名为 reprfunc
。
函数签名 reprfunc
定义在 Include/cpython/object.h
中,它只有一个参数 PyObject*(self)
:
例如,cellobject
使用函数 cell_repr
实现了 tp_repr
函数槽:
PyTypeObject
中除了这些以 tp_
为前缀的的基本类型槽外,还有其他类型槽定义:
类型槽 | 前缀 |
---|---|
|
|
|
|
|
|
|
|
|
|
每个类型槽都有一个独特的编号,定义在 Include/typeslots.h
。当引用或获取一个对象的类型槽时,你应该使用这些常量。
例如,tp_repr
对应的常量为 Py_tp_repr
,它的值是 66
,其始终与类型槽的位置相匹配。这些常量在检查一个对象是否实现了一个特定的类型槽函数时非常有用。
在 C 语言中使用类型
在 C 扩展模块和 CPython 核心代码中,你会经常使用 PyObject*
类型。
举个例子,如果你在一个可取下标的对象(例如一个列表或字符串)上执行 x[n]
,则会调用 PyObject_GetItem()
,该函数会查看对象 x
以决定如何对其进行取下标操作。
Objects/abstract.c
第 146 行
PyObject_GetItem()
可以用于映射类型,例如字典,也可以用于序列类型,例如列表和元组。
如果实例 o
有序列方法,那么 o->ob_type->tp_as_sequence
会被求值为真。如果实例定义了 sq_item
类型槽函数,那么就可以假设它正确实现了序列协议。
检查对象 key
是否可以被转成整数,然后使用 PySequence_GetItem()
从序列对象中取出元素。
类型属性字典
Python 支持使用 class
关键字定义新的类型。用户定义的类型会由类型对象模块中的 type_new()
创建。
用户定义类型有一个属性字典,可以使用 __dict__()
获取。每当在一个自定义类上访问属性时,__getattr__()
的默认实现是在这个属性字典中查找。类方法、实例方法、类属性和实例属性都在这个字典中。
PyObject_GenericGetDict()
实现了获取一个对象字典实例的逻辑。PyObject_GetAttr()
是 __getattr__()
的默认实现,同样 PyObject_SetAttr()
是 __setattr__()
的默认实现。
参见
关于自定义类型有很多层次,这方便有大量的文档。关于 metaclass 就可以写一整本书,但是在本书中,只关注其实现。
如果你想学习更多关于元编程的内容,可以参考 Real Python 的 “Python Metaclasses”。
Last updated