“Python/docs/3.9/extending/newtypes tutorial”的版本间差异

来自菜鸟教程
Python/docs/3.9/extending/newtypes tutorial
跳转至:导航、​搜索
(autoload)
 
(Page commit)
 
第1行: 第1行:
 +
{{DISPLAYTITLE:2. 定义扩展类型:教程 — Python 文档}}
 
<div id="defining-extension-types-tutorial" class="section">
 
<div id="defining-extension-types-tutorial" class="section">
  
 
<span id="defining-new-types"></span>
 
<span id="defining-new-types"></span>
= <span class="section-number">2. </span>Defining Extension Types: Tutorial =
+
= 2. 定义扩展类型:教程 =
  
Python allows the writer of a C extension module to define new types that
+
Python 允许 C 扩展模块的编写者定义可以从 Python 代码操作的新类型,很像内置的 [[../../library/stdtypes#str|str]] [[../../library/stdtypes#list|list]] 类型。 所有扩展类型的代码都遵循一个模式,但在开始之前,您需要了解一些细节。 本文档是对该主题的温和介绍。
can be manipulated from Python code, much like the built-in [[../../library/stdtypes#str|<code>str</code>]]
 
and [[../../library/stdtypes#list|<code>list</code>]] types. The code for all extension types follows a
 
pattern, but there are some details that you need to understand before you
 
can get started. This document is a gentle introduction to the topic.
 
  
 
<div id="the-basics" class="section">
 
<div id="the-basics" class="section">
  
 
<span id="dnt-basics"></span>
 
<span id="dnt-basics"></span>
== <span class="section-number">2.1. </span>The Basics ==
+
== 2.1. 基础知识 ==
  
The [[../../glossary#term-CPython|<span class="xref std std-term">CPython</span>]] runtime sees all Python objects as variables of type
+
[[../../glossary#term-CPython|CPython]] 运行时将所有 Python 对象视为 <span class="xref c c-texpr">PyObject*</span> 类型的变量,作为所有 Python 对象的“基本类型”。 [[../../c-api/structures#c|PyObject]] 结构本身只包含对象的 [[../../glossary#term-reference-count|引用计数]] 和一个指向对象“类型对象”的指针。 这就是行动所在; 类型对象确定解释器调用哪些 (C) 函数,例如,在对象上查找属性、调用方法或将其与另一个对象相乘。 这些 C 函数称为“类型方法”。
<span class="xref c c-texpr">[[../../c-api/structures#c|PyObject]]*</span>, which serves as a &quot;base type&quot; for all Python objects.
 
The [[../../c-api/structures#c|<code>PyObject</code>]] structure itself only contains the object's
 
[[../../glossary#term-reference-count|<span class="xref std std-term">reference count</span>]] and a pointer to the object's &quot;type object&quot;.
 
This is where the action is; the type object determines which (C) functions
 
get called by the interpreter when, for instance, an attribute gets looked up
 
on an object, a method called, or it is multiplied by another object. These
 
C functions are called &quot;type methods&quot;.
 
  
So, if you want to define a new extension type, you need to create a new type
+
所以,如果你想定义一个新的扩展类型,你需要创建一个新的类型对象。
object.
 
  
This sort of thing can only be explained by example, so here's a minimal, but
+
这种事情只能通过例子来解释,所以这里有一个最小但完整的模块,它在 C 扩展模块 <code>custom</code> 中定义了一个名为 <code>Custom</code> 的新类型:
complete, module that defines a new type named <code>Custom</code> inside a C
 
extension module <code>custom</code>:
 
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
What we're showing here is the traditional way of defining ''static''
+
我们在这里展示的是定义 ''static'' 扩展类型的传统方法。 它应该足以满足大多数用途。 C API 还允许使用 [[../../c-api/type#c|PyType_FromSpec()]] 函数定义堆分配的扩展类型,本教程未涵盖该函数。
extension types. It should be adequate for most uses. The C API also
 
allows defining heap-allocated extension types using the
 
[[../../c-api/type#c|<code>PyType_FromSpec()</code>]] function, which isn't covered in this tutorial.
 
  
  
第46行: 第30行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>#define PY_SSIZE_T_CLEAN
+
<syntaxhighlight lang="c">#define PY_SSIZE_T_CLEAN
#include &lt;Python.h&gt;
+
#include <Python.h>
  
 
typedef struct {
 
typedef struct {
第56行: 第40行:
 
static PyTypeObject CustomType = {
 
static PyTypeObject CustomType = {
 
     PyVarObject_HEAD_INIT(NULL, 0)
 
     PyVarObject_HEAD_INIT(NULL, 0)
     .tp_name = &quot;custom.Custom&quot;,
+
     .tp_name = "custom.Custom",
     .tp_doc = &quot;Custom objects&quot;,
+
     .tp_doc = "Custom objects",
 
     .tp_basicsize = sizeof(CustomObject),
 
     .tp_basicsize = sizeof(CustomObject),
 
     .tp_itemsize = 0,
 
     .tp_itemsize = 0,
第66行: 第50行:
 
static PyModuleDef custommodule = {
 
static PyModuleDef custommodule = {
 
     PyModuleDef_HEAD_INIT,
 
     PyModuleDef_HEAD_INIT,
     .m_name = &quot;custom&quot;,
+
     .m_name = "custom",
     .m_doc = &quot;Example module that creates an extension type.&quot;,
+
     .m_doc = "Example module that creates an extension type.",
 
     .m_size = -1,
 
     .m_size = -1,
 
};
 
};
第75行: 第59行:
 
{
 
{
 
     PyObject *m;
 
     PyObject *m;
     if (PyType_Ready(&amp;CustomType) &lt; 0)
+
     if (PyType_Ready(&CustomType) < 0)
 
         return NULL;
 
         return NULL;
  
     m = PyModule_Create(&amp;custommodule);
+
     m = PyModule_Create(&custommodule);
 
     if (m == NULL)
 
     if (m == NULL)
 
         return NULL;
 
         return NULL;
  
     Py_INCREF(&amp;CustomType);
+
     Py_INCREF(&CustomType);
     if (PyModule_AddObject(m, &quot;Custom&quot;, (PyObject *) &amp;CustomType) &lt; 0) {
+
     if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
         Py_DECREF(&amp;CustomType);
+
         Py_DECREF(&CustomType);
 
         Py_DECREF(m);
 
         Py_DECREF(m);
 
         return NULL;
 
         return NULL;
第90行: 第74行:
  
 
     return m;
 
     return m;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Now that's quite a bit to take in at once, but hopefully bits will seem familiar
+
现在一下子就明白了很多,但希望上一章的内容看起来很熟悉。 这个文件定义了三件事:
from the previous chapter. This file defines three things:
 
  
# What a <code>Custom</code> '''object''' contains: this is the <code>CustomObject</code> struct, which is allocated once for each <code>Custom</code> instance.
+
# <code>Custom</code> '''object''' 包含什么:这是 <code>CustomObject</code> 结构体,它为每个 <code>Custom</code> 实例分配一次。
# How the <code>Custom</code> '''type''' behaves: this is the <code>CustomType</code> struct, which defines a set of flags and function pointers that the interpreter inspects when specific operations are requested.
+
# <code>Custom</code> '''type''' 的行为方式:这是 <code>CustomType</code> 结构体,它定义了解释器在请求特定操作时检查的一组标志和函数指针。
# How to initialize the <code>custom</code> module: this is the <code>PyInit_custom</code> function and the associated <code>custommodule</code> struct.
+
# 如何初始化 <code>custom</code> 模块:这是 <code>PyInit_custom</code> 函数和关联的 <code>custommodule</code> 结构。
  
The first bit is:
+
第一点是:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第108行: 第91行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>typedef struct {
+
<syntaxhighlight lang="c">typedef struct {
 
     PyObject_HEAD
 
     PyObject_HEAD
} CustomObject;</pre>
+
} CustomObject;</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This is what a Custom object will contain. <code>PyObject_HEAD</code> is mandatory
+
这是自定义对象将包含的内容。 <code>PyObject_HEAD</code> 在每个对象结构的开头是强制性的,它定义了一个名为 <code>ob_base</code> 的字段,类型为 [[../../c-api/structures#c|PyObject]],包含一个指向类型对象的指针和一个引用计数(这些可以分别使用宏 [[../../c-api/structures#c|Py_REFCNT]] [[../../c-api/structures#c|Py_TYPE]] 访问)。 使用宏的原因是抽象布局并在调试版本中启用其他字段。
at the start of each object struct and defines a field called <code>ob_base</code>
 
of type [[../../c-api/structures#c|<code>PyObject</code>]], containing a pointer to a type object and a
 
reference count (these can be accessed using the macros [[../../c-api/structures#c|<code>Py_REFCNT</code>]]
 
and [[../../c-api/structures#c|<code>Py_TYPE</code>]] respectively). The reason for the macro is to
 
abstract away the layout and to enable additional fields in debug builds.
 
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
There is no semicolon above after the [[../../c-api/structures#c|<code>PyObject_HEAD</code>]] macro.
+
[[../../c-api/structures#c|PyObject_HEAD]] 宏后面没有分号。 小心添加一个意外:一些编译器会抱怨。
Be wary of adding one by accident: some compilers will complain.
 
  
  
 
</div>
 
</div>
Of course, objects generally store additional data besides the standard
+
当然,除了标准的 <code>PyObject_HEAD</code> 样板之外,对象通常还存储额外的数据; 例如,这里是标准 Python 浮点数的定义:
<code>PyObject_HEAD</code> boilerplate; for example, here is the definition for
 
standard Python floats:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第139行: 第114行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>typedef struct {
+
<syntaxhighlight lang="c">typedef struct {
 
     PyObject_HEAD
 
     PyObject_HEAD
 
     double ob_fval;
 
     double ob_fval;
} PyFloatObject;</pre>
+
} PyFloatObject;</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The second bit is the definition of the type object.
+
第二位是类型对象的定义。
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第153行: 第128行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static PyTypeObject CustomType = {
+
<syntaxhighlight lang="c">static PyTypeObject CustomType = {
 
     PyVarObject_HEAD_INIT(NULL, 0)
 
     PyVarObject_HEAD_INIT(NULL, 0)
     .tp_name = &quot;custom.Custom&quot;,
+
     .tp_name = "custom.Custom",
     .tp_doc = &quot;Custom objects&quot;,
+
     .tp_doc = "Custom objects",
 
     .tp_basicsize = sizeof(CustomObject),
 
     .tp_basicsize = sizeof(CustomObject),
 
     .tp_itemsize = 0,
 
     .tp_itemsize = 0,
 
     .tp_flags = Py_TPFLAGS_DEFAULT,
 
     .tp_flags = Py_TPFLAGS_DEFAULT,
 
     .tp_new = PyType_GenericNew,
 
     .tp_new = PyType_GenericNew,
};</pre>
+
};</syntaxhighlight>
  
 
</div>
 
</div>
第168行: 第143行:
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
We recommend using C99-style designated initializers as above, to
+
我们建议使用上述 C99 风格的指定初始值设定项,以避免列出您不关心的所有 [[../../c-api/type#c|PyTypeObject]] 字段,也避免关心字段的声明顺序。
avoid listing all the [[../../c-api/type#c|<code>PyTypeObject</code>]] fields that you don't care
 
about and also to avoid caring about the fields' declaration order.
 
  
  
 
</div>
 
</div>
The actual definition of [[../../c-api/type#c|<code>PyTypeObject</code>]] in <code>object.h</code> has
+
<code>object.h</code>中[[../../c-api/type#c|PyTypeObject]]的实际定义比上面的定义有更多的[[../../c-api/typeobj#type-structs|字段]]。 其余字段将由 C 编译器用零填充,除非您需要它们,否则通常不显式指定它们。
many more [[../../c-api/typeobj#type-structs|<span class="std std-ref">fields</span>]] than the definition above. The
 
remaining fields will be filled with zeros by the C compiler, and it's
 
common practice to not specify them explicitly unless you need them.
 
  
We're going to pick it apart, one field at a time:
+
我们将把它分开,一次一个领域:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第187行: 第157行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>PyVarObject_HEAD_INIT(NULL, 0)</pre>
+
<syntaxhighlight lang="c">PyVarObject_HEAD_INIT(NULL, 0)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This line is mandatory boilerplate to initialize the <code>ob_base</code>
+
此行是初始化上述 <code>ob_base</code> 字段的强制性样板。
field mentioned above.
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第199行: 第168行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_name = &quot;custom.Custom&quot;,</pre>
+
<syntaxhighlight lang="c">.tp_name = "custom.Custom",</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The name of our type. This will appear in the default textual representation of
+
我们类型的名称。 这将出现在我们对象的默认文本表示和一些错误消息中,例如:
our objects and in some error messages, for example:
 
  
 
<div class="highlight-pycon notranslate">
 
<div class="highlight-pycon notranslate">
第211行: 第179行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; &quot;&quot; + custom.Custom()
+
<syntaxhighlight lang="pycon">>>> "" + custom.Custom()
 
Traceback (most recent call last):
 
Traceback (most recent call last):
   File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
+
   File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not &quot;custom.Custom&quot;) to str</pre>
+
TypeError: can only concatenate str (not "custom.Custom") to str</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Note that the name is a dotted name that includes both the module name and the
+
请注意,名称是一个带点的名称,包括模块名称和模块内的类型名称。 本例中的模块为<code>custom</code>,类型为<code>Custom</code>,因此我们将类型名称设置为<code>custom.Custom</code>。 使用真正的虚线导入路径对于使您的类型与 [[../../library/pydoc#module-pydoc|pydoc]] [[../../library/pickle#module-pickle|pickle]] 模块兼容很重要。
name of the type within the module. The module in this case is <code>custom</code> and
 
the type is <code>Custom</code>, so we set the type name to <code>custom.Custom</code>.
 
Using the real dotted import path is important to make your type compatible
 
with the [[../../library/pydoc#module-pydoc|<code>pydoc</code>]] and [[../../library/pickle#module-pickle|<code>pickle</code>]] modules.
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第229行: 第193行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_basicsize = sizeof(CustomObject),
+
<syntaxhighlight lang="c">.tp_basicsize = sizeof(CustomObject),
.tp_itemsize = 0,</pre>
+
.tp_itemsize = 0,</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This is so that Python knows how much memory to allocate when creating
+
这是为了让 Python 在创建新的 <code>Custom</code> 实例时知道要分配多少内存。 [[../../c-api/typeobj#c.PyTypeObject|tp_itemsize]] 仅用于可变大小的对象,否则应为零。
new <code>Custom</code> instances. [[../../c-api/typeobj#c.PyTypeObject|<code>tp_itemsize</code>]] is
 
only used for variable-sized objects and should otherwise be zero.
 
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
If you want your type to be subclassable from Python, and your type has the same
+
如果您希望您的类型可以从 Python 继承,并且您的类型与它的基类型具有相同的 [[../../c-api/typeobj#c.PyTypeObject|tp_basicsize]],那么您可能会遇到多重继承的问题。 您类型的 Python 子类必须首先在其 [[../../library/stdtypes#class|__bases__]] 中列出您的类型,否则它将无法调用您的类型的 <code>__new__()</code> 方法而不出现错误。 您可以通过确保您的类型的 [[../../c-api/typeobj#c.PyTypeObject|tp_basicsize]] 值大于其基本类型的值来避免此问题。 大多数情况下,无论如何这都是正确的,因为要么您的基类型将是 [[../../library/functions#object|object]],要么您将向基类型添加数据成员,从而增加其大小。
[[../../c-api/typeobj#c.PyTypeObject|<code>tp_basicsize</code>]] as its base type, you may have problems with multiple
 
inheritance. A Python subclass of your type will have to list your type first
 
in its [[../../library/stdtypes#class|<code>__bases__</code>]], or else it will not be able to call your type's
 
<code>__new__()</code> method without getting an error. You can avoid this problem by
 
ensuring that your type has a larger value for [[../../c-api/typeobj#c.PyTypeObject|<code>tp_basicsize</code>]] than its
 
base type does. Most of the time, this will be true anyway, because either your
 
base type will be [[../../library/functions#object|<code>object</code>]], or else you will be adding data members to
 
your base type, and therefore increasing its size.
 
  
  
 
</div>
 
</div>
We set the class flags to [[../../c-api/typeobj#Py_TPFLAGS_DEFAULT|<code>Py_TPFLAGS_DEFAULT</code>]].
+
我们将类标志设置为 [[../../c-api/typeobj#Py_TPFLAGS_DEFAULT|Py_TPFLAGS_DEFAULT]]
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第261行: 第215行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_flags = Py_TPFLAGS_DEFAULT,</pre>
+
<syntaxhighlight lang="c">.tp_flags = Py_TPFLAGS_DEFAULT,</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
All types should include this constant in their flags. It enables all of the
+
所有类型都应在其标志中包含此常量。 它启用至少在 Python 3.3 之前定义的所有成员。 如果您需要更多成员,则需要对相应的标志进行 OR 运算。
members defined until at least Python 3.3. If you need further members,
 
you will need to OR the corresponding flags.
 
  
We provide a doc string for the type in [[../../c-api/typeobj#c.PyTypeObject|<code>tp_doc</code>]].
+
我们为 [[../../c-api/typeobj#c.PyTypeObject|tp_doc]] 中的类型提供了一个文档字符串。
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第276行: 第228行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_doc = &quot;Custom objects&quot;,</pre>
+
<syntaxhighlight lang="c">.tp_doc = "Custom objects",</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
To enable object creation, we have to provide a [[../../c-api/typeobj#c.PyTypeObject|<code>tp_new</code>]]
+
要启用对象创建,我们必须提供 [[../../c-api/typeobj#c.PyTypeObject|tp_new]] 处理程序。 这相当于 Python 方法 <code>__new__()</code>,但必须明确指定。 在这种情况下,我们可以使用 API 函数 [[../../c-api/type#c|PyType_GenericNew()]] 提供的默认实现。
handler. This is the equivalent of the Python method <code>__new__()</code>, but
 
has to be specified explicitly. In this case, we can just use the default
 
implementation provided by the API function [[../../c-api/type#c|<code>PyType_GenericNew()</code>]].
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第290行: 第239行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_new = PyType_GenericNew,</pre>
+
<syntaxhighlight lang="c">.tp_new = PyType_GenericNew,</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Everything else in the file should be familiar, except for some code in
+
除了 <code>PyInit_custom()</code> 中的一些代码之外,文件中的其他所有内容都应该很熟悉:
<code>PyInit_custom()</code>:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第302行: 第250行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>if (PyType_Ready(&amp;CustomType) &lt; 0)
+
<syntaxhighlight lang="c">if (PyType_Ready(&CustomType) < 0)
     return;</pre>
+
     return;</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This initializes the <code>Custom</code> type, filling in a number of members
+
这将初始化 <code>Custom</code> 类型,将一些成员填充为适当的默认值,包括我们最初设置为 <code>NULL</code> <code>ob_type</code>
to the appropriate default values, including <code>ob_type</code> that we initially
 
set to <code>NULL</code>.
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第316行: 第262行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>Py_INCREF(&amp;CustomType);
+
<syntaxhighlight lang="c">Py_INCREF(&CustomType);
if (PyModule_AddObject(m, &quot;Custom&quot;, (PyObject *) &amp;CustomType) &lt; 0) {
+
if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
     Py_DECREF(&amp;CustomType);
+
     Py_DECREF(&CustomType);
 
     Py_DECREF(m);
 
     Py_DECREF(m);
 
     return NULL;
 
     return NULL;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This adds the type to the module dictionary. This allows us to create
+
这会将类型添加到模块字典中。 这允许我们通过调用 <code>Custom</code> 类来创建 <code>Custom</code> 实例:
<code>Custom</code> instances by calling the <code>Custom</code> class:
 
  
 
<div class="highlight-pycon notranslate">
 
<div class="highlight-pycon notranslate">
第333行: 第278行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; import custom
+
<syntaxhighlight lang="pycon">>>> import custom
&gt;&gt;&gt; mycustom = custom.Custom()</pre>
+
>>> mycustom = custom.Custom()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
That's it! All that remains is to build it; put the above code in a file called
+
就是这样! 剩下的就是建造它; 将上面的代码放在一个名为 <code>custom.c</code> 的文件中,然后:
<code>custom.c</code> and:
 
  
 
<div class="highlight-python notranslate">
 
<div class="highlight-python notranslate">
第346行: 第290行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from distutils.core import setup, Extension
+
<syntaxhighlight lang="python">from distutils.core import setup, Extension
setup(name=&quot;custom&quot;, version=&quot;1.0&quot;,
+
setup(name="custom", version="1.0",
       ext_modules=[Extension(&quot;custom&quot;, [&quot;custom.c&quot;])])</pre>
+
       ext_modules=[Extension("custom", ["custom.c"])])</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
in a file called <code>setup.py</code>; then typing
+
在一个名为 <code>setup.py</code> 的文件中; 然后打字
  
 
<div class="highlight-shell-session notranslate">
 
<div class="highlight-shell-session notranslate">
第359行: 第303行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>$ python setup.py build</pre>
+
<pre class="session">$ python setup.py build</pre>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
at a shell should produce a file <code>custom.so</code> in a subdirectory; move to
+
shell 中应该在子目录中生成一个文件 <code>custom.so</code>; 移至该目录并启动 Python — 您应该能够 <code>import custom</code> 并使用自定义对象。
that directory and fire up Python --- you should be able to <code>import custom</code> and
 
play around with Custom objects.
 
  
That wasn't so hard, was it?
+
那没那么难,是吗?
  
Of course, the current Custom type is pretty uninteresting. It has no data and
+
当然,当前的自定义类型非常无趣。 它没有数据,也不做任何事情。 它甚至不能被子类化。
doesn't do anything. It can't even be subclassed.
 
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
While this documentation showcases the standard [[../../library/distutils#module-distutils|<code>distutils</code>]] module
+
虽然本文档展示了用于构建 C 扩展的标准 [[../../library/distutils#module-distutils|distutils]] 模块,但建议在实际用例中使用更新且维护更好的 <code>setuptools</code> 库。 有关如何执行此操作的文档超出了本文档的范围,可以在 [https://packaging.python.org/tutorials/distributing-packages/ Python 打包用户指南] 中找到。
for building C extensions, it is recommended in real-world use cases to
 
use the newer and better-maintained <code>setuptools</code> library. Documentation
 
on how to do this is out of scope for this document and can be found in
 
the [https://packaging.python.org/tutorials/distributing-packages/ Python Packaging User's Guide].
 
  
  
第389行: 第326行:
 
<div id="adding-data-and-methods-to-the-basic-example" class="section">
 
<div id="adding-data-and-methods-to-the-basic-example" class="section">
  
== <span class="section-number">2.2. </span>Adding data and methods to the Basic example ==
+
== 2.2. Basic 示例添加数据和方法 ==
  
Let's extend the basic example to add some data and methods. Let's also make
+
让我们扩展基本示例以添加一些数据和方法。 让我们也使该类型可用作基类。 我们将创建一个新模块 <code>custom2</code> 来添加以下功能:
the type usable as a base class. We'll create a new module, <code>custom2</code> that
 
adds these capabilities:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第399行: 第334行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>#define PY_SSIZE_T_CLEAN
+
<syntaxhighlight lang="c">#define PY_SSIZE_T_CLEAN
#include &lt;Python.h&gt;
+
#include <Python.h>
#include &quot;structmember.h&quot;
+
#include "structmember.h"
  
 
typedef struct {
 
typedef struct {
第413行: 第348行:
 
Custom_dealloc(CustomObject *self)
 
Custom_dealloc(CustomObject *self)
 
{
 
{
     Py_XDECREF(self-&gt;first);
+
     Py_XDECREF(self->first);
     Py_XDECREF(self-&gt;last);
+
     Py_XDECREF(self->last);
     Py_TYPE(self)-&gt;tp_free((PyObject *) self);
+
     Py_TYPE(self)->tp_free((PyObject *) self);
 
}
 
}
  
第422行: 第357行:
 
{
 
{
 
     CustomObject *self;
 
     CustomObject *self;
     self = (CustomObject *) type-&gt;tp_alloc(type, 0);
+
     self = (CustomObject *) type->tp_alloc(type, 0);
 
     if (self != NULL) {
 
     if (self != NULL) {
         self-&gt;first = PyUnicode_FromString(&quot;&quot;);
+
         self->first = PyUnicode_FromString("");
         if (self-&gt;first == NULL) {
+
         if (self->first == NULL) {
 
             Py_DECREF(self);
 
             Py_DECREF(self);
 
             return NULL;
 
             return NULL;
 
         }
 
         }
         self-&gt;last = PyUnicode_FromString(&quot;&quot;);
+
         self->last = PyUnicode_FromString("");
         if (self-&gt;last == NULL) {
+
         if (self->last == NULL) {
 
             Py_DECREF(self);
 
             Py_DECREF(self);
 
             return NULL;
 
             return NULL;
 
         }
 
         }
         self-&gt;number = 0;
+
         self->number = 0;
 
     }
 
     }
 
     return (PyObject *) self;
 
     return (PyObject *) self;
第442行: 第377行:
 
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
 
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
 
{
 
{
     static char *kwlist[] = {&quot;first&quot;, &quot;last&quot;, &quot;number&quot;, NULL};
+
     static char *kwlist[] = {"first", "last", "number", NULL};
 
     PyObject *first = NULL, *last = NULL, *tmp;
 
     PyObject *first = NULL, *last = NULL, *tmp;
  
     if (!PyArg_ParseTupleAndKeywords(args, kwds, &quot;|OOi&quot;, kwlist,
+
     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
                                     &amp;first, &amp;last,
+
                                     &first, &last,
                                     &amp;self-&gt;number))
+
                                     &self->number))
 
         return -1;
 
         return -1;
  
 
     if (first) {
 
     if (first) {
         tmp = self-&gt;first;
+
         tmp = self->first;
 
         Py_INCREF(first);
 
         Py_INCREF(first);
         self-&gt;first = first;
+
         self->first = first;
 
         Py_XDECREF(tmp);
 
         Py_XDECREF(tmp);
 
     }
 
     }
 
     if (last) {
 
     if (last) {
         tmp = self-&gt;last;
+
         tmp = self->last;
 
         Py_INCREF(last);
 
         Py_INCREF(last);
         self-&gt;last = last;
+
         self->last = last;
 
         Py_XDECREF(tmp);
 
         Py_XDECREF(tmp);
 
     }
 
     }
第466行: 第401行:
  
 
static PyMemberDef Custom_members[] = {
 
static PyMemberDef Custom_members[] = {
     {&quot;first&quot;, T_OBJECT_EX, offsetof(CustomObject, first), 0,
+
     {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
     &quot;first name&quot;},
+
     "first name"},
     {&quot;last&quot;, T_OBJECT_EX, offsetof(CustomObject, last), 0,
+
     {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,
     &quot;last name&quot;},
+
     "last name"},
     {&quot;number&quot;, T_INT, offsetof(CustomObject, number), 0,
+
     {"number", T_INT, offsetof(CustomObject, number), 0,
     &quot;custom number&quot;},
+
     "custom number"},
 
     {NULL}  /* Sentinel */
 
     {NULL}  /* Sentinel */
 
};
 
};
第478行: 第413行:
 
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
 
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
 
{
 
{
     if (self-&gt;first == NULL) {
+
     if (self->first == NULL) {
         PyErr_SetString(PyExc_AttributeError, &quot;first&quot;);
+
         PyErr_SetString(PyExc_AttributeError, "first");
 
         return NULL;
 
         return NULL;
 
     }
 
     }
     if (self-&gt;last == NULL) {
+
     if (self->last == NULL) {
         PyErr_SetString(PyExc_AttributeError, &quot;last&quot;);
+
         PyErr_SetString(PyExc_AttributeError, "last");
 
         return NULL;
 
         return NULL;
 
     }
 
     }
     return PyUnicode_FromFormat(&quot;%S %S&quot;, self-&gt;first, self-&gt;last);
+
     return PyUnicode_FromFormat("%S %S", self->first, self->last);
 
}
 
}
  
 
static PyMethodDef Custom_methods[] = {
 
static PyMethodDef Custom_methods[] = {
     {&quot;name&quot;, (PyCFunction) Custom_name, METH_NOARGS,
+
     {"name", (PyCFunction) Custom_name, METH_NOARGS,
     &quot;Return the name, combining the first and last name&quot;
+
     "Return the name, combining the first and last name"
 
     },
 
     },
 
     {NULL}  /* Sentinel */
 
     {NULL}  /* Sentinel */
第498行: 第433行:
 
static PyTypeObject CustomType = {
 
static PyTypeObject CustomType = {
 
     PyVarObject_HEAD_INIT(NULL, 0)
 
     PyVarObject_HEAD_INIT(NULL, 0)
     .tp_name = &quot;custom2.Custom&quot;,
+
     .tp_name = "custom2.Custom",
     .tp_doc = &quot;Custom objects&quot;,
+
     .tp_doc = "Custom objects",
 
     .tp_basicsize = sizeof(CustomObject),
 
     .tp_basicsize = sizeof(CustomObject),
 
     .tp_itemsize = 0,
 
     .tp_itemsize = 0,
第512行: 第447行:
 
static PyModuleDef custommodule = {
 
static PyModuleDef custommodule = {
 
     PyModuleDef_HEAD_INIT,
 
     PyModuleDef_HEAD_INIT,
     .m_name = &quot;custom2&quot;,
+
     .m_name = "custom2",
     .m_doc = &quot;Example module that creates an extension type.&quot;,
+
     .m_doc = "Example module that creates an extension type.",
 
     .m_size = -1,
 
     .m_size = -1,
 
};
 
};
第521行: 第456行:
 
{
 
{
 
     PyObject *m;
 
     PyObject *m;
     if (PyType_Ready(&amp;CustomType) &lt; 0)
+
     if (PyType_Ready(&CustomType) < 0)
 
         return NULL;
 
         return NULL;
  
     m = PyModule_Create(&amp;custommodule);
+
     m = PyModule_Create(&custommodule);
 
     if (m == NULL)
 
     if (m == NULL)
 
         return NULL;
 
         return NULL;
  
     Py_INCREF(&amp;CustomType);
+
     Py_INCREF(&CustomType);
     if (PyModule_AddObject(m, &quot;Custom&quot;, (PyObject *) &amp;CustomType) &lt; 0) {
+
     if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
         Py_DECREF(&amp;CustomType);
+
         Py_DECREF(&CustomType);
 
         Py_DECREF(m);
 
         Py_DECREF(m);
 
         return NULL;
 
         return NULL;
第536行: 第471行:
  
 
     return m;
 
     return m;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This version of the module has a number of changes.
+
此版本的模块有许多更改。
  
We've added an extra include:
+
我们添加了一个额外的包括:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第549行: 第484行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>#include &lt;structmember.h&gt;</pre>
+
<syntaxhighlight lang="c">#include <structmember.h></syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This include provides declarations that we use to handle attributes, as
+
这包括提供我们用来处理属性的声明,如稍后所述。
described a bit later.
 
  
The <code>Custom</code> type now has three data attributes in its C struct,
+
<code>Custom</code> 类型现在在其 C 结构中具有三个数据属性,''first''''last'' ''number''''first'' ''last'' 变量是包含名字和姓氏的 Python 字符串。 ''number'' 属性是一个 C 整数。
''first'', ''last'', and ''number''. The ''first'' and ''last'' variables are Python
 
strings containing first and last names. The ''number'' attribute is a C integer.
 
  
The object structure is updated accordingly:
+
对象结构会相应更新:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第567行: 第499行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>typedef struct {
+
<syntaxhighlight lang="c">typedef struct {
 
     PyObject_HEAD
 
     PyObject_HEAD
 
     PyObject *first; /* first name */
 
     PyObject *first; /* first name */
 
     PyObject *last;  /* last name */
 
     PyObject *last;  /* last name */
 
     int number;
 
     int number;
} CustomObject;</pre>
+
} CustomObject;</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Because we now have data to manage, we have to be more careful about object
+
因为我们现在有数据要管理,所以我们必须更加小心地分配和释放对象。 至少,我们需要一个释放方法:
allocation and deallocation. At a minimum, we need a deallocation method:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第584行: 第515行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static void
+
<syntaxhighlight lang="c">static void
 
Custom_dealloc(CustomObject *self)
 
Custom_dealloc(CustomObject *self)
 
{
 
{
     Py_XDECREF(self-&gt;first);
+
     Py_XDECREF(self->first);
     Py_XDECREF(self-&gt;last);
+
     Py_XDECREF(self->last);
     Py_TYPE(self)-&gt;tp_free((PyObject *) self);
+
     Py_TYPE(self)->tp_free((PyObject *) self);
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
which is assigned to the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_dealloc</code>]] member:
+
分配给 [[../../c-api/typeobj#c.PyTypeObject|tp_dealloc]] 成员:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第601行: 第532行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_dealloc = (destructor) Custom_dealloc,</pre>
+
<syntaxhighlight lang="c">.tp_dealloc = (destructor) Custom_dealloc,</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
This method first clears the reference counts of the two Python attributes.
+
此方法首先清除两个 Python 属性的引用计数。 [[../../c-api/refcounting#c|Py_XDECREF()]] 正确处理其参数为 <code>NULL</code> 的情况(如果 <code>tp_new</code> 中途失败,可能会发生这种情况)。 然后它调用对象类型的 [[../../c-api/typeobj#c.PyTypeObject|tp_free]] 成员(由 <code>Py_TYPE(self)</code> 计算)来释放对象的内存。 请注意,对象的类型可能不是 <code>CustomType</code>,因为该对象可能是子类的实例。
[[../../c-api/refcounting#c|<code>Py_XDECREF()</code>]] correctly handles the case where its argument is
 
<code>NULL</code> (which might happen here if <code>tp_new</code> failed midway). It then
 
calls the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_free</code>]] member of the object's type
 
(computed by <code>Py_TYPE(self)</code>) to free the object's memory. Note that
 
the object's type might not be <code>CustomType</code>, because the object may
 
be an instance of a subclass.
 
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
The explicit cast to <code>destructor</code> above is needed because we defined
+
需要显式转换为上面的 <code>destructor</code>,因为我们定义了 <code>Custom_dealloc</code> 来接受一个 <code>CustomObject *</code> 参数,但是 <code>tp_dealloc</code> 函数指针期望接收一个 <code>PyObject *</code> 参数。 否则,编译器会发出警告。 这是面向对象的多态性,在 C 中!
<code>Custom_dealloc</code> to take a <code>CustomObject *</code> argument, but the <code>tp_dealloc</code>
 
function pointer expects to receive a <code>PyObject *</code> argument. Otherwise,
 
the compiler will emit a warning. This is object-oriented polymorphism,
 
in C!
 
  
  
 
</div>
 
</div>
We want to make sure that the first and last names are initialized to empty
+
我们要确保名字和姓氏被初始化为空字符串,因此我们提供了一个 <code>tp_new</code> 实现:
strings, so we provide a <code>tp_new</code> implementation:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第633行: 第553行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static PyObject *
+
<syntaxhighlight lang="c">static PyObject *
 
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
{
 
{
 
     CustomObject *self;
 
     CustomObject *self;
     self = (CustomObject *) type-&gt;tp_alloc(type, 0);
+
     self = (CustomObject *) type->tp_alloc(type, 0);
 
     if (self != NULL) {
 
     if (self != NULL) {
         self-&gt;first = PyUnicode_FromString(&quot;&quot;);
+
         self->first = PyUnicode_FromString("");
         if (self-&gt;first == NULL) {
+
         if (self->first == NULL) {
 
             Py_DECREF(self);
 
             Py_DECREF(self);
 
             return NULL;
 
             return NULL;
 
         }
 
         }
         self-&gt;last = PyUnicode_FromString(&quot;&quot;);
+
         self->last = PyUnicode_FromString("");
         if (self-&gt;last == NULL) {
+
         if (self->last == NULL) {
 
             Py_DECREF(self);
 
             Py_DECREF(self);
 
             return NULL;
 
             return NULL;
 
         }
 
         }
         self-&gt;number = 0;
+
         self->number = 0;
 
     }
 
     }
 
     return (PyObject *) self;
 
     return (PyObject *) self;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
and install it in the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_new</code>]] member:
+
并将其安装在 [[../../c-api/typeobj#c.PyTypeObject|tp_new]] 成员中:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第663行: 第583行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_new = Custom_new,</pre>
+
<syntaxhighlight lang="c">.tp_new = Custom_new,</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The <code>tp_new</code> handler is responsible for creating (as opposed to initializing)
+
<code>tp_new</code> 处理程序负责创建(而不是初始化)该类型的对象。 它在 Python 中公开为 <code>__new__()</code> 方法。 不需要定义 <code>tp_new</code> 成员,实际上许多扩展类型将简单地重用 [[../../c-api/type#c|PyType_GenericNew()]],就像在上面的 <code>Custom</code> 类型的第一个版本中所做的那样。 在这种情况下,我们使用 <code>tp_new</code> 处理程序将 <code>first</code> <code>last</code> 属性初始化为非 <code>NULL</code> 默认值。
objects of the type. It is exposed in Python as the <code>__new__()</code> method.
 
It is not required to define a <code>tp_new</code> member, and indeed many extension
 
types will simply reuse [[../../c-api/type#c|<code>PyType_GenericNew()</code>]] as done in the first
 
version of the <code>Custom</code> type above. In this case, we use the <code>tp_new</code>
 
handler to initialize the <code>first</code> and <code>last</code> attributes to non-<code>NULL</code>
 
default values.
 
  
<code>tp_new</code> is passed the type being instantiated (not necessarily <code>CustomType</code>,
+
<code>tp_new</code> 被传递正在被实例化的类型(不一定是 <code>CustomType</code>,如果一个子类被实例化)和在调用该类型时传递的任何参数,并期望返回创建的实例。 <code>tp_new</code> 处理程序总是接受位置和关键字参数,但他们经常忽略这些参数,将参数处理留给初始化程序(又名 C 中的 <code>tp_init</code> 或 Python 中的 <code>__init__</code>)方法。
if a subclass is instantiated) and any arguments passed when the type was
 
called, and is expected to return the instance created. <code>tp_new</code> handlers
 
always accept positional and keyword arguments, but they often ignore the
 
arguments, leaving the argument handling to initializer (a.k.a. <code>tp_init</code>
 
in C or <code>__init__</code> in Python) methods.
 
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
<code>tp_new</code> shouldn't call <code>tp_init</code> explicitly, as the interpreter
+
<code>tp_new</code> 不应显式调用 <code>tp_init</code>,因为解释器会自己调用。
will do it itself.
 
  
  
 
</div>
 
</div>
The <code>tp_new</code> implementation calls the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_alloc</code>]]
+
<code>tp_new</code> 实现调用 [[../../c-api/typeobj#c.PyTypeObject|tp_alloc]] 槽来分配内存:
slot to allocate memory:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第699行: 第606行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>self = (CustomObject *) type-&gt;tp_alloc(type, 0);</pre>
+
<syntaxhighlight lang="c">self = (CustomObject *) type->tp_alloc(type, 0);</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Since memory allocation may fail, we must check the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_alloc</code>]]
+
由于内存分配可能会失败,因此在继续之前,我们必须根据 <code>NULL</code> 检查 [[../../c-api/typeobj#c.PyTypeObject|tp_alloc]] 结果。
result against <code>NULL</code> before proceeding.
 
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
We didn't fill the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_alloc</code>]] slot ourselves. Rather
+
我们没有自己填充 [[../../c-api/typeobj#c.PyTypeObject|tp_alloc]] 插槽。 而是 [[../../c-api/type#c|PyType_Ready()]] 通过从我们的基类继承它来为我们填充它,默认情况下它是 [[../../library/functions#object|object]]。 大多数类型使用默认分配策略。
[[../../c-api/type#c|<code>PyType_Ready()</code>]] fills it for us by inheriting it from our base class,
 
which is [[../../library/functions#object|<code>object</code>]] by default. Most types use the default allocation
 
strategy.
 
  
  
第720行: 第623行:
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
If you are creating a co-operative [[../../c-api/typeobj#c.PyTypeObject|<code>tp_new</code>]] (one
+
如果您正在创建一个合作社 [[../../c-api/typeobj#c.PyTypeObject|tp_new]](一个调用基本类型的 [[../../c-api/typeobj#c.PyTypeObject|tp_new]] <code>__new__()</code>),您必须 ''not'' 尝试确定在运行时使用方法解析顺序调用什么方法。 始终静态地确定您要调用的类型,并直接调用其 [[../../c-api/typeobj#c.PyTypeObject|tp_new]],或通过 <code>type-&gt;tp_base-&gt;tp_new</code>。 如果不这样做,您的类型的 Python 子类也从其他 Python 定义的类继承可能无法正常工作。 (具体来说,您可能无法在没有得到 [[../../library/exceptions#TypeError|TypeError]] 的情况下创建此类子类的实例。)
that calls a base type's [[../../c-api/typeobj#c.PyTypeObject|<code>tp_new</code>]] or <code>__new__()</code>),
 
you must ''not'' try to determine what method to call using method resolution
 
order at runtime. Always statically determine what type you are going to
 
call, and call its [[../../c-api/typeobj#c.PyTypeObject|<code>tp_new</code>]] directly, or via
 
<code>type-&gt;tp_base-&gt;tp_new</code>. If you do not do this, Python subclasses of your
 
type that also inherit from other Python-defined classes may not work correctly.
 
(Specifically, you may not be able to create instances of such subclasses
 
without getting a [[../../library/exceptions#TypeError|<code>TypeError</code>]].)
 
  
  
 
</div>
 
</div>
We also define an initialization function which accepts arguments to provide
+
我们还定义了一个初始化函数,它接受参数来为我们的实例提供初始值:
initial values for our instance:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第741行: 第635行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static int
+
<syntaxhighlight lang="c">static int
 
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
 
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
 
{
 
{
     static char *kwlist[] = {&quot;first&quot;, &quot;last&quot;, &quot;number&quot;, NULL};
+
     static char *kwlist[] = {"first", "last", "number", NULL};
 
     PyObject *first = NULL, *last = NULL, *tmp;
 
     PyObject *first = NULL, *last = NULL, *tmp;
  
     if (!PyArg_ParseTupleAndKeywords(args, kwds, &quot;|OOi&quot;, kwlist,
+
     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
                                     &amp;first, &amp;last,
+
                                     &first, &last,
                                     &amp;self-&gt;number))
+
                                     &self->number))
 
         return -1;
 
         return -1;
  
 
     if (first) {
 
     if (first) {
         tmp = self-&gt;first;
+
         tmp = self->first;
 
         Py_INCREF(first);
 
         Py_INCREF(first);
         self-&gt;first = first;
+
         self->first = first;
 
         Py_XDECREF(tmp);
 
         Py_XDECREF(tmp);
 
     }
 
     }
 
     if (last) {
 
     if (last) {
         tmp = self-&gt;last;
+
         tmp = self->last;
 
         Py_INCREF(last);
 
         Py_INCREF(last);
         self-&gt;last = last;
+
         self->last = last;
 
         Py_XDECREF(tmp);
 
         Py_XDECREF(tmp);
 
     }
 
     }
 
     return 0;
 
     return 0;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
by filling the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_init</code>]] slot.
+
通过填充 [[../../c-api/typeobj#c.PyTypeObject|tp_init]] 槽。
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第776行: 第670行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_init = (initproc) Custom_init,</pre>
+
<syntaxhighlight lang="c">.tp_init = (initproc) Custom_init,</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The [[../../c-api/typeobj#c.PyTypeObject|<code>tp_init</code>]] slot is exposed in Python as the
+
[[../../c-api/typeobj#c.PyTypeObject|tp_init]] 槽在 Python 中公开为 <code>__init__()</code> 方法。 它用于在创建对象后对其进行初始化。 初始化器总是接受位置和关键字参数,并且它们应该在成功时返回 <code>0</code> 或在错误时返回 <code>-1</code>
<code>__init__()</code> method. It is used to initialize an object after it's
 
created. Initializers always accept positional and keyword arguments,
 
and they should return either <code>0</code> on success or <code>-1</code> on error.
 
  
Unlike the <code>tp_new</code> handler, there is no guarantee that <code>tp_init</code>
+
<code>tp_new</code> 处理程序不同,根本无法保证调用 <code>tp_init</code>(例如,[[../../library/pickle#module-pickle|pickle]] 模块默认不调用 <code>__init__()</code>在未腌制的情况下)。 也可以多次调用。 任何人都可以在我们的对象上调用 <code>__init__()</code> 方法。 出于这个原因,我们在分配新的属性值时必须格外小心。 我们可能会受到诱惑,例如像这样分配 <code>first</code> 成员:
is called at all (for example, the [[../../library/pickle#module-pickle|<code>pickle</code>]] module by default
 
doesn't call <code>__init__()</code> on unpickled instances). It can also be
 
called multiple times. Anyone can call the <code>__init__()</code> method on
 
our objects. For this reason, we have to be extra careful when assigning
 
the new attribute values. We might be tempted, for example to assign the
 
<code>first</code> member like this:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第798行: 第683行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>if (first) {
+
<syntaxhighlight lang="c">if (first) {
     Py_XDECREF(self-&gt;first);
+
     Py_XDECREF(self->first);
 
     Py_INCREF(first);
 
     Py_INCREF(first);
     self-&gt;first = first;
+
     self->first = first;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
But this would be risky. Our type doesn't restrict the type of the
+
但这将是有风险的。 我们的类型不限制 <code>first</code> 成员的类型,所以它可以是任何类型的对象。 它可能有一个析构函数,导致尝试访问 <code>first</code> 成员的代码被执行; 或者那个析构函数可以释放 [[../../glossary#term-GIL|全局解释器锁]] 并让任意代码在访问和修改我们对象的其他线程中运行。
<code>first</code> member, so it could be any kind of object. It could have a
 
destructor that causes code to be executed that tries to access the
 
<code>first</code> member; or that destructor could release the
 
[[../../glossary#term-GIL|<span class="xref std std-term">Global interpreter Lock</span>]] and let arbitrary code run in other
 
threads that accesses and modifies our object.
 
  
To be paranoid and protect ourselves against this possibility, we almost
+
为了偏执并保护自己免受这种可能性的影响,我们几乎总是在减少成员的引用计数之前重新分配成员。 我们什么时候不需要这样做?
always reassign members before decrementing their reference counts. When
 
don't we have to do this?
 
  
* when we absolutely know that the reference count is greater than 1;
+
* 当我们绝对知道引用计数大于 1 时;
* when we know that deallocation of the object [[#id5|1]] will neither release the [[../../glossary#term-GIL|<span class="xref std std-term">GIL</span>]] nor cause any calls back into our type's code;
+
* 当我们知道对象 [[#id5|1]] 的释放既不会释放 [[../../glossary#term-GIL|GIL]] 也不会导致任何回调到我们类型的代码中时;
* when decrementing a reference count in a [[../../c-api/typeobj#c.PyTypeObject|<code>tp_dealloc</code>]] handler on a type which doesn't support cyclic garbage collection [[#id6|2]].
+
* 在不支持循环垃圾回收 [[#id6|2]] 的类型的 [[../../c-api/typeobj#c.PyTypeObject|tp_dealloc]] 处理程序中递减引用计数时。
  
We want to expose our instance variables as attributes. There are a
+
我们想将我们的实例变量公开为属性。 有很多方法可以做到这一点。 最简单的方法是定义成员定义:
number of ways to do that. The simplest way is to define member definitions:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第829行: 第706行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static PyMemberDef Custom_members[] = {
+
<syntaxhighlight lang="c">static PyMemberDef Custom_members[] = {
     {&quot;first&quot;, T_OBJECT_EX, offsetof(CustomObject, first), 0,
+
     {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
     &quot;first name&quot;},
+
     "first name"},
     {&quot;last&quot;, T_OBJECT_EX, offsetof(CustomObject, last), 0,
+
     {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,
     &quot;last name&quot;},
+
     "last name"},
     {&quot;number&quot;, T_INT, offsetof(CustomObject, number), 0,
+
     {"number", T_INT, offsetof(CustomObject, number), 0,
     &quot;custom number&quot;},
+
     "custom number"},
 
     {NULL}  /* Sentinel */
 
     {NULL}  /* Sentinel */
};</pre>
+
};</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
and put the definitions in the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_members</code>]] slot:
+
并将定义放在 [[../../c-api/typeobj#c.PyTypeObject|tp_members]] 插槽中:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第848行: 第725行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_members = Custom_members,</pre>
+
<syntaxhighlight lang="c">.tp_members = Custom_members,</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Each member definition has a member name, type, offset, access flags and
+
每个成员定义都有一个成员名称、类型、偏移量、访问标志和文档字符串。 有关详细信息,请参阅下面的 [[../newtypes#generic-attribute-management|通用属性管理]] 部分。
documentation string. See the [[../newtypes#generic-attribute-management|<span class="std std-ref">Generic Attribute Management</span>]] section
 
below for details.
 
  
A disadvantage of this approach is that it doesn't provide a way to restrict the
+
这种方法的一个缺点是它没有提供一种方法来限制可以分配给 Python 属性的对象类型。 我们希望名字和姓氏是字符串,但可以分配任何 Python 对象。 此外,可以删除属性,将 C 指针设置为 <code>NULL</code>。 即使我们可以确保成员被初始化为非 <code>NULL</code> 值,如果删除属性,成员可以设置为 <code>NULL</code>
types of objects that can be assigned to the Python attributes. We expect the
 
first and last names to be strings, but any Python objects can be assigned.
 
Further, the attributes can be deleted, setting the C pointers to <code>NULL</code>. Even
 
though we can make sure the members are initialized to non-<code>NULL</code> values, the
 
members can be set to <code>NULL</code> if the attributes are deleted.
 
  
We define a single method, <code>Custom.name()</code>, that outputs the objects name as the
+
我们定义了一个方法,<code>Custom.name()</code>,它将对象名称输出为名字和姓氏的串联。
concatenation of the first and last names.
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第871行: 第740行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static PyObject *
+
<syntaxhighlight lang="c">static PyObject *
 
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
 
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
 
{
 
{
     if (self-&gt;first == NULL) {
+
     if (self->first == NULL) {
         PyErr_SetString(PyExc_AttributeError, &quot;first&quot;);
+
         PyErr_SetString(PyExc_AttributeError, "first");
 
         return NULL;
 
         return NULL;
 
     }
 
     }
     if (self-&gt;last == NULL) {
+
     if (self->last == NULL) {
         PyErr_SetString(PyExc_AttributeError, &quot;last&quot;);
+
         PyErr_SetString(PyExc_AttributeError, "last");
 
         return NULL;
 
         return NULL;
 
     }
 
     }
     return PyUnicode_FromFormat(&quot;%S %S&quot;, self-&gt;first, self-&gt;last);
+
     return PyUnicode_FromFormat("%S %S", self->first, self->last);
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The method is implemented as a C function that takes a <code>Custom</code> (or
+
该方法被实现为一个 C 函数,它采用 <code>Custom</code>(或 <code>Custom</code> 子类)实例作为第一个参数。 方法总是将一个实例作为第一个参数。 方法通常也接受位置参数和关键字参数,但在这种情况下,我们不接受任何参数,也不需要接受位置参数元组或关键字参数字典。 此方法等效于 Python 方法:
<code>Custom</code> subclass) instance as the first argument. Methods always take an
 
instance as the first argument. Methods often take positional and keyword
 
arguments as well, but in this case we don't take any and don't need to accept
 
a positional argument tuple or keyword argument dictionary. This method is
 
equivalent to the Python method:
 
  
 
<div class="highlight-python notranslate">
 
<div class="highlight-python notranslate">
第899行: 第763行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>def name(self):
+
<syntaxhighlight lang="python">def name(self):
     return &quot;%s %s&quot; % (self.first, self.last)</pre>
+
     return "%s %s" % (self.first, self.last)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Note that we have to check for the possibility that our <code>first</code> and
+
请注意,我们必须检查我们的 <code>first</code> <code>last</code> 成员是 <code>NULL</code> 的可能性。 这是因为它们可以被删除,在这种情况下它们被设置为 <code>NULL</code>。 最好防止删除这些属性并将属性值限制为字符串。 我们将在下一节中看到如何做到这一点。
<code>last</code> members are <code>NULL</code>. This is because they can be deleted, in which
 
case they are set to <code>NULL</code>. It would be better to prevent deletion of these
 
attributes and to restrict the attribute values to be strings. We'll see how to
 
do that in the next section.
 
  
Now that we've defined the method, we need to create an array of method
+
现在我们已经定义了方法,我们需要创建一个方法定义数组:
definitions:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第918行: 第777行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static PyMethodDef Custom_methods[] = {
+
<syntaxhighlight lang="c">static PyMethodDef Custom_methods[] = {
     {&quot;name&quot;, (PyCFunction) Custom_name, METH_NOARGS,
+
     {"name", (PyCFunction) Custom_name, METH_NOARGS,
     &quot;Return the name, combining the first and last name&quot;
+
     "Return the name, combining the first and last name"
 
     },
 
     },
 
     {NULL}  /* Sentinel */
 
     {NULL}  /* Sentinel */
};</pre>
+
};</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
(note that we used the [[../../c-api/structures#METH_NOARGS|<code>METH_NOARGS</code>]] flag to indicate that the method
+
(请注意,我们使用 [[../../c-api/structures#METH_NOARGS|METH_NOARGS]] 标志来指示该方法除了 ''self'' 之外没有其他参数)
is expecting no arguments other than ''self'')
 
  
and assign it to the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_methods</code>]] slot:
+
并将其分配给 [[../../c-api/typeobj#c.PyTypeObject|tp_methods]] 插槽:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第937行: 第795行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_methods = Custom_methods,</pre>
+
<syntaxhighlight lang="c">.tp_methods = Custom_methods,</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Finally, we'll make our type usable as a base class for subclassing. We've
+
最后,我们将使我们的类型可用作子类化的基类。 到目前为止,我们已经仔细地编写了我们的方法,因此它们不会对正在创建或使用的对象的类型做出任何假设,所以我们需要做的就是将 [[../../c-api/typeobj#Py_TPFLAGS_BASETYPE|Py_TPFLAGS_BASETYPE]] 添加到我们的类标志定义:
written our methods carefully so far so that they don't make any assumptions
 
about the type of the object being created or used, so all we need to do is
 
to add the [[../../c-api/typeobj#Py_TPFLAGS_BASETYPE|<code>Py_TPFLAGS_BASETYPE</code>]] to our class flag definition:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第951行: 第806行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,</pre>
+
<syntaxhighlight lang="c">.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
We rename <code>PyInit_custom()</code> to <code>PyInit_custom2()</code>, update the
+
我们将 <code>PyInit_custom()</code> 重命名为 <code>PyInit_custom2()</code>,更新 [[../../c-api/module#c|PyModuleDef]] 结构中的模块名称,并更新 [[../../c-api/type#c|PyTypeObject]] 结构中的完整类名。
module name in the [[../../c-api/module#c|<code>PyModuleDef</code>]] struct, and update the full class
 
name in the [[../../c-api/type#c|<code>PyTypeObject</code>]] struct.
 
  
Finally, we update our <code>setup.py</code> file to build the new module:
+
最后,我们更新 <code>setup.py</code> 文件以构建新模块:
  
 
<div class="highlight-python notranslate">
 
<div class="highlight-python notranslate">
第966行: 第819行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from distutils.core import setup, Extension
+
<syntaxhighlight lang="python">from distutils.core import setup, Extension
setup(name=&quot;custom&quot;, version=&quot;1.0&quot;,
+
setup(name="custom", version="1.0",
 
       ext_modules=[
 
       ext_modules=[
         Extension(&quot;custom&quot;, [&quot;custom.c&quot;]),
+
         Extension("custom", ["custom.c"]),
         Extension(&quot;custom2&quot;, [&quot;custom2.c&quot;]),
+
         Extension("custom2", ["custom2.c"]),
         ])</pre>
+
         ])</syntaxhighlight>
  
 
</div>
 
</div>
第980行: 第833行:
 
<div id="providing-finer-control-over-data-attributes" class="section">
 
<div id="providing-finer-control-over-data-attributes" class="section">
  
== <span class="section-number">2.3. </span>Providing finer control over data attributes ==
+
== 2.3. 提供对数据属性的更精细控制 ==
  
In this section, we'll provide finer control over how the <code>first</code> and
+
在本节中,我们将更好地控制如何在 <code>Custom</code> 示例中设置 <code>first</code> <code>last</code> 属性。 在我们模块的先前版本中,实例变量 <code>first</code> <code>last</code> 可以设置为非字符串值甚至删除。 我们要确保这些属性始终包含字符串。
<code>last</code> attributes are set in the <code>Custom</code> example. In the previous
 
version of our module, the instance variables <code>first</code> and <code>last</code>
 
could be set to non-string values or even deleted. We want to make sure that
 
these attributes always contain strings.
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第992行: 第841行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>#define PY_SSIZE_T_CLEAN
+
<syntaxhighlight lang="c">#define PY_SSIZE_T_CLEAN
#include &lt;Python.h&gt;
+
#include <Python.h>
#include &quot;structmember.h&quot;
+
#include "structmember.h"
  
 
typedef struct {
 
typedef struct {
第1,006行: 第855行:
 
Custom_dealloc(CustomObject *self)
 
Custom_dealloc(CustomObject *self)
 
{
 
{
     Py_XDECREF(self-&gt;first);
+
     Py_XDECREF(self->first);
     Py_XDECREF(self-&gt;last);
+
     Py_XDECREF(self->last);
     Py_TYPE(self)-&gt;tp_free((PyObject *) self);
+
     Py_TYPE(self)->tp_free((PyObject *) self);
 
}
 
}
  
第1,015行: 第864行:
 
{
 
{
 
     CustomObject *self;
 
     CustomObject *self;
     self = (CustomObject *) type-&gt;tp_alloc(type, 0);
+
     self = (CustomObject *) type->tp_alloc(type, 0);
 
     if (self != NULL) {
 
     if (self != NULL) {
         self-&gt;first = PyUnicode_FromString(&quot;&quot;);
+
         self->first = PyUnicode_FromString("");
         if (self-&gt;first == NULL) {
+
         if (self->first == NULL) {
 
             Py_DECREF(self);
 
             Py_DECREF(self);
 
             return NULL;
 
             return NULL;
 
         }
 
         }
         self-&gt;last = PyUnicode_FromString(&quot;&quot;);
+
         self->last = PyUnicode_FromString("");
         if (self-&gt;last == NULL) {
+
         if (self->last == NULL) {
 
             Py_DECREF(self);
 
             Py_DECREF(self);
 
             return NULL;
 
             return NULL;
 
         }
 
         }
         self-&gt;number = 0;
+
         self->number = 0;
 
     }
 
     }
 
     return (PyObject *) self;
 
     return (PyObject *) self;
第1,035行: 第884行:
 
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
 
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
 
{
 
{
     static char *kwlist[] = {&quot;first&quot;, &quot;last&quot;, &quot;number&quot;, NULL};
+
     static char *kwlist[] = {"first", "last", "number", NULL};
 
     PyObject *first = NULL, *last = NULL, *tmp;
 
     PyObject *first = NULL, *last = NULL, *tmp;
  
     if (!PyArg_ParseTupleAndKeywords(args, kwds, &quot;|UUi&quot;, kwlist,
+
     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
                                     &amp;first, &amp;last,
+
                                     &first, &last,
                                     &amp;self-&gt;number))
+
                                     &self->number))
 
         return -1;
 
         return -1;
  
 
     if (first) {
 
     if (first) {
         tmp = self-&gt;first;
+
         tmp = self->first;
 
         Py_INCREF(first);
 
         Py_INCREF(first);
         self-&gt;first = first;
+
         self->first = first;
 
         Py_DECREF(tmp);
 
         Py_DECREF(tmp);
 
     }
 
     }
 
     if (last) {
 
     if (last) {
         tmp = self-&gt;last;
+
         tmp = self->last;
 
         Py_INCREF(last);
 
         Py_INCREF(last);
         self-&gt;last = last;
+
         self->last = last;
 
         Py_DECREF(tmp);
 
         Py_DECREF(tmp);
 
     }
 
     }
第1,059行: 第908行:
  
 
static PyMemberDef Custom_members[] = {
 
static PyMemberDef Custom_members[] = {
     {&quot;number&quot;, T_INT, offsetof(CustomObject, number), 0,
+
     {"number", T_INT, offsetof(CustomObject, number), 0,
     &quot;custom number&quot;},
+
     "custom number"},
 
     {NULL}  /* Sentinel */
 
     {NULL}  /* Sentinel */
 
};
 
};
第1,067行: 第916行:
 
Custom_getfirst(CustomObject *self, void *closure)
 
Custom_getfirst(CustomObject *self, void *closure)
 
{
 
{
     Py_INCREF(self-&gt;first);
+
     Py_INCREF(self->first);
     return self-&gt;first;
+
     return self->first;
 
}
 
}
  
第1,076行: 第925行:
 
     PyObject *tmp;
 
     PyObject *tmp;
 
     if (value == NULL) {
 
     if (value == NULL) {
         PyErr_SetString(PyExc_TypeError, &quot;Cannot delete the first attribute&quot;);
+
         PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
 
         return -1;
 
         return -1;
 
     }
 
     }
 
     if (!PyUnicode_Check(value)) {
 
     if (!PyUnicode_Check(value)) {
 
         PyErr_SetString(PyExc_TypeError,
 
         PyErr_SetString(PyExc_TypeError,
                         &quot;The first attribute value must be a string&quot;);
+
                         "The first attribute value must be a string");
 
         return -1;
 
         return -1;
 
     }
 
     }
     tmp = self-&gt;first;
+
     tmp = self->first;
 
     Py_INCREF(value);
 
     Py_INCREF(value);
     self-&gt;first = value;
+
     self->first = value;
 
     Py_DECREF(tmp);
 
     Py_DECREF(tmp);
 
     return 0;
 
     return 0;
第1,094行: 第943行:
 
Custom_getlast(CustomObject *self, void *closure)
 
Custom_getlast(CustomObject *self, void *closure)
 
{
 
{
     Py_INCREF(self-&gt;last);
+
     Py_INCREF(self->last);
     return self-&gt;last;
+
     return self->last;
 
}
 
}
  
第1,103行: 第952行:
 
     PyObject *tmp;
 
     PyObject *tmp;
 
     if (value == NULL) {
 
     if (value == NULL) {
         PyErr_SetString(PyExc_TypeError, &quot;Cannot delete the last attribute&quot;);
+
         PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
 
         return -1;
 
         return -1;
 
     }
 
     }
 
     if (!PyUnicode_Check(value)) {
 
     if (!PyUnicode_Check(value)) {
 
         PyErr_SetString(PyExc_TypeError,
 
         PyErr_SetString(PyExc_TypeError,
                         &quot;The last attribute value must be a string&quot;);
+
                         "The last attribute value must be a string");
 
         return -1;
 
         return -1;
 
     }
 
     }
     tmp = self-&gt;last;
+
     tmp = self->last;
 
     Py_INCREF(value);
 
     Py_INCREF(value);
     self-&gt;last = value;
+
     self->last = value;
 
     Py_DECREF(tmp);
 
     Py_DECREF(tmp);
 
     return 0;
 
     return 0;
第1,119行: 第968行:
  
 
static PyGetSetDef Custom_getsetters[] = {
 
static PyGetSetDef Custom_getsetters[] = {
     {&quot;first&quot;, (getter) Custom_getfirst, (setter) Custom_setfirst,
+
     {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
     &quot;first name&quot;, NULL},
+
     "first name", NULL},
     {&quot;last&quot;, (getter) Custom_getlast, (setter) Custom_setlast,
+
     {"last", (getter) Custom_getlast, (setter) Custom_setlast,
     &quot;last name&quot;, NULL},
+
     "last name", NULL},
 
     {NULL}  /* Sentinel */
 
     {NULL}  /* Sentinel */
 
};
 
};
第1,129行: 第978行:
 
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
 
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
 
{
 
{
     return PyUnicode_FromFormat(&quot;%S %S&quot;, self-&gt;first, self-&gt;last);
+
     return PyUnicode_FromFormat("%S %S", self->first, self->last);
 
}
 
}
  
 
static PyMethodDef Custom_methods[] = {
 
static PyMethodDef Custom_methods[] = {
     {&quot;name&quot;, (PyCFunction) Custom_name, METH_NOARGS,
+
     {"name", (PyCFunction) Custom_name, METH_NOARGS,
     &quot;Return the name, combining the first and last name&quot;
+
     "Return the name, combining the first and last name"
 
     },
 
     },
 
     {NULL}  /* Sentinel */
 
     {NULL}  /* Sentinel */
第1,141行: 第990行:
 
static PyTypeObject CustomType = {
 
static PyTypeObject CustomType = {
 
     PyVarObject_HEAD_INIT(NULL, 0)
 
     PyVarObject_HEAD_INIT(NULL, 0)
     .tp_name = &quot;custom3.Custom&quot;,
+
     .tp_name = "custom3.Custom",
     .tp_doc = &quot;Custom objects&quot;,
+
     .tp_doc = "Custom objects",
 
     .tp_basicsize = sizeof(CustomObject),
 
     .tp_basicsize = sizeof(CustomObject),
 
     .tp_itemsize = 0,
 
     .tp_itemsize = 0,
第1,156行: 第1,005行:
 
static PyModuleDef custommodule = {
 
static PyModuleDef custommodule = {
 
     PyModuleDef_HEAD_INIT,
 
     PyModuleDef_HEAD_INIT,
     .m_name = &quot;custom3&quot;,
+
     .m_name = "custom3",
     .m_doc = &quot;Example module that creates an extension type.&quot;,
+
     .m_doc = "Example module that creates an extension type.",
 
     .m_size = -1,
 
     .m_size = -1,
 
};
 
};
第1,165行: 第1,014行:
 
{
 
{
 
     PyObject *m;
 
     PyObject *m;
     if (PyType_Ready(&amp;CustomType) &lt; 0)
+
     if (PyType_Ready(&CustomType) < 0)
 
         return NULL;
 
         return NULL;
  
     m = PyModule_Create(&amp;custommodule);
+
     m = PyModule_Create(&custommodule);
 
     if (m == NULL)
 
     if (m == NULL)
 
         return NULL;
 
         return NULL;
  
     Py_INCREF(&amp;CustomType);
+
     Py_INCREF(&CustomType);
     if (PyModule_AddObject(m, &quot;Custom&quot;, (PyObject *) &amp;CustomType) &lt; 0) {
+
     if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
         Py_DECREF(&amp;CustomType);
+
         Py_DECREF(&CustomType);
 
         Py_DECREF(m);
 
         Py_DECREF(m);
 
         return NULL;
 
         return NULL;
第1,180行: 第1,029行:
  
 
     return m;
 
     return m;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
To provide greater control, over the <code>first</code> and <code>last</code> attributes,
+
为了更好地控制 <code>first</code> <code>last</code> 属性,我们将使用自定义的 getter setter 函数。 以下是获取和设置 <code>first</code> 属性的函数:
we'll use custom getter and setter functions. Here are the functions for
 
getting and setting the <code>first</code> attribute:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,193行: 第1,040行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static PyObject *
+
<syntaxhighlight lang="c">static PyObject *
 
Custom_getfirst(CustomObject *self, void *closure)
 
Custom_getfirst(CustomObject *self, void *closure)
 
{
 
{
     Py_INCREF(self-&gt;first);
+
     Py_INCREF(self->first);
     return self-&gt;first;
+
     return self->first;
 
}
 
}
  
第1,205行: 第1,052行:
 
     PyObject *tmp;
 
     PyObject *tmp;
 
     if (value == NULL) {
 
     if (value == NULL) {
         PyErr_SetString(PyExc_TypeError, &quot;Cannot delete the first attribute&quot;);
+
         PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
 
         return -1;
 
         return -1;
 
     }
 
     }
 
     if (!PyUnicode_Check(value)) {
 
     if (!PyUnicode_Check(value)) {
 
         PyErr_SetString(PyExc_TypeError,
 
         PyErr_SetString(PyExc_TypeError,
                         &quot;The first attribute value must be a string&quot;);
+
                         "The first attribute value must be a string");
 
         return -1;
 
         return -1;
 
     }
 
     }
     tmp = self-&gt;first;
+
     tmp = self->first;
 
     Py_INCREF(value);
 
     Py_INCREF(value);
     self-&gt;first = value;
+
     self->first = value;
 
     Py_DECREF(tmp);
 
     Py_DECREF(tmp);
 
     return 0;
 
     return 0;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The getter function is passed a <code>Custom</code> object and a &quot;closure&quot;, which is
+
getter 函数被传递一个 <code>Custom</code> 对象和一个“闭包”,它是一个空指针。 在这种情况下,闭包将被忽略。 (闭包支持将定义数据传递给 getter setter 的高级用法。 例如,这可以用于允许一组 getter setter 函数根据闭包中的数据决定要获取或设置的属性。)
a void pointer. In this case, the closure is ignored. (The closure supports an
 
advanced usage in which definition data is passed to the getter and setter. This
 
could, for example, be used to allow a single set of getter and setter functions
 
that decide the attribute to get or set based on data in the closure.)
 
  
The setter function is passed the <code>Custom</code> object, the new value, and the
+
setter 函数传递 <code>Custom</code> 对象、新值和闭包。 新值可能是 <code>NULL</code>,在这种情况下,该属性将被删除。 在我们的 setter 中,如果属性被删除或者它的新值不是字符串,我们会引发错误。
closure. The new value may be <code>NULL</code>, in which case the attribute is being
 
deleted. In our setter, we raise an error if the attribute is deleted or if its
 
new value is not a string.
 
  
We create an array of [[../../c-api/structures#c|<code>PyGetSetDef</code>]] structures:
+
我们创建一个 [[../../c-api/structures#c|PyGetSetDef]] 结构数组:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,240行: 第1,080行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static PyGetSetDef Custom_getsetters[] = {
+
<syntaxhighlight lang="c">static PyGetSetDef Custom_getsetters[] = {
     {&quot;first&quot;, (getter) Custom_getfirst, (setter) Custom_setfirst,
+
     {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
     &quot;first name&quot;, NULL},
+
     "first name", NULL},
     {&quot;last&quot;, (getter) Custom_getlast, (setter) Custom_setlast,
+
     {"last", (getter) Custom_getlast, (setter) Custom_setlast,
     &quot;last name&quot;, NULL},
+
     "last name", NULL},
 
     {NULL}  /* Sentinel */
 
     {NULL}  /* Sentinel */
};</pre>
+
};</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
and register it in the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_getset</code>]] slot:
+
并将其注册到 [[../../c-api/typeobj#c.PyTypeObject|tp_getset]] 插槽中:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,257行: 第1,097行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_getset = Custom_getsetters,</pre>
+
<syntaxhighlight lang="c">.tp_getset = Custom_getsetters,</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The last item in a [[../../c-api/structures#c|<code>PyGetSetDef</code>]] structure is the &quot;closure&quot; mentioned
+
[[../../c-api/structures#c|PyGetSetDef]] 结构中的最后一项是上面提到的“闭包”。 在这种情况下,我们没有使用闭包,所以我们只传递 <code>NULL</code>
above. In this case, we aren't using a closure, so we just pass <code>NULL</code>.
 
  
We also remove the member definitions for these attributes:
+
我们还删除了这些属性的成员定义:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,271行: 第1,110行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static PyMemberDef Custom_members[] = {
+
<syntaxhighlight lang="c">static PyMemberDef Custom_members[] = {
     {&quot;number&quot;, T_INT, offsetof(CustomObject, number), 0,
+
     {"number", T_INT, offsetof(CustomObject, number), 0,
     &quot;custom number&quot;},
+
     "custom number"},
 
     {NULL}  /* Sentinel */
 
     {NULL}  /* Sentinel */
};</pre>
+
};</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
We also need to update the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_init</code>]] handler to only
+
我们还需要更新 [[../../c-api/typeobj#c.PyTypeObject|tp_init]] 处理程序以只允许传递字符串 [[#id7|3]]
allow strings [[#id7|3]] to be passed:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,287行: 第1,125行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static int
+
<syntaxhighlight lang="c">static int
 
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
 
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
 
{
 
{
     static char *kwlist[] = {&quot;first&quot;, &quot;last&quot;, &quot;number&quot;, NULL};
+
     static char *kwlist[] = {"first", "last", "number", NULL};
 
     PyObject *first = NULL, *last = NULL, *tmp;
 
     PyObject *first = NULL, *last = NULL, *tmp;
  
     if (!PyArg_ParseTupleAndKeywords(args, kwds, &quot;|UUi&quot;, kwlist,
+
     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
                                     &amp;first, &amp;last,
+
                                     &first, &last,
                                     &amp;self-&gt;number))
+
                                     &self->number))
 
         return -1;
 
         return -1;
  
 
     if (first) {
 
     if (first) {
         tmp = self-&gt;first;
+
         tmp = self->first;
 
         Py_INCREF(first);
 
         Py_INCREF(first);
         self-&gt;first = first;
+
         self->first = first;
 
         Py_DECREF(tmp);
 
         Py_DECREF(tmp);
 
     }
 
     }
 
     if (last) {
 
     if (last) {
         tmp = self-&gt;last;
+
         tmp = self->last;
 
         Py_INCREF(last);
 
         Py_INCREF(last);
         self-&gt;last = last;
+
         self->last = last;
 
         Py_DECREF(tmp);
 
         Py_DECREF(tmp);
 
     }
 
     }
 
     return 0;
 
     return 0;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
With these changes, we can assure that the <code>first</code> and <code>last</code> members are
+
通过这些更改,我们可以确保 <code>first</code> <code>last</code> 成员永远不会是 <code>NULL</code>,因此我们可以在几乎所有情况下删除对 <code>NULL</code> 值的检查。 这意味着大部分 [[../../c-api/refcounting#c|Py_XDECREF()]] 调用可以转换为 [[../../c-api/refcounting#c|Py_DECREF()]] 调用。 我们唯一不能改变这些调用的地方是在 <code>tp_dealloc</code> 实现中,这里有可能在 <code>tp_new</code> 中这些成员的初始化失败。
never <code>NULL</code> so we can remove checks for <code>NULL</code> values in almost all cases.
 
This means that most of the [[../../c-api/refcounting#c|<code>Py_XDECREF()</code>]] calls can be converted to
 
[[../../c-api/refcounting#c|<code>Py_DECREF()</code>]] calls. The only place we can't change these calls is in
 
the <code>tp_dealloc</code> implementation, where there is the possibility that the
 
initialization of these members failed in <code>tp_new</code>.
 
  
We also rename the module initialization function and module name in the
+
我们还在初始化函数中重命名了模块初始化函数和模块名称,就像我们之前所做的那样,我们在 <code>setup.py</code> 文件中添加了一个额外的定义。
initialization function, as we did before, and we add an extra definition to the
 
<code>setup.py</code> file.
 
  
  
第1,331行: 第1,162行:
 
<div id="supporting-cyclic-garbage-collection" class="section">
 
<div id="supporting-cyclic-garbage-collection" class="section">
  
== <span class="section-number">2.4. </span>Supporting cyclic garbage collection ==
+
== 2.4. 支持循环垃圾回收 ==
  
Python has a [[../../glossary#term-garbage-collection|<span class="xref std std-term">cyclic garbage collector (GC)</span>]] that
+
Python 有一个 [[../../glossary#term-garbage-collection|循环垃圾收集器 (GC)]],即使它们的引用计数不为零,它也可以识别不需要的对象。 当对象参与循环时,就会发生这种情况。 例如,考虑:
can identify unneeded objects even when their reference counts are not zero.
 
This can happen when objects are involved in cycles. For example, consider:
 
  
 
<div class="highlight-pycon notranslate">
 
<div class="highlight-pycon notranslate">
第1,341行: 第1,170行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; l = []
+
<syntaxhighlight lang="pycon">>>> l = []
&gt;&gt;&gt; l.append(l)
+
>>> l.append(l)
&gt;&gt;&gt; del l</pre>
+
>>> del l</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
In this example, we create a list that contains itself. When we delete it, it
+
在本例中,我们创建了一个包含自身的列表。 当我们删除它时,它仍然有一个来自它自己的引用。 它的引用计数不会降到零。 幸运的是,Python 的循环垃圾收集器最终会发现列表是垃圾并释放它。
still has a reference from itself. Its reference count doesn't drop to zero.
 
Fortunately, Python's cyclic garbage collector will eventually figure out that
 
the list is garbage and free it.
 
  
In the second version of the <code>Custom</code> example, we allowed any kind of
+
<code>Custom</code> 示例的第二个版本中,我们允许将任何类型的对象存储在 <code>first</code> <code>last</code> 属性 [[#id8|4]] 中。 此外,在第二和第三个版本中,我们允许子类化<code>Custom</code>,子类可以添加任意属性。 由于这两个原因中的任何一个,<code>Custom</code> 对象可以参与循环:
object to be stored in the <code>first</code> or <code>last</code> attributes [[#id8|4]].
 
Besides, in the second and third versions, we allowed subclassing
 
<code>Custom</code>, and subclasses may add arbitrary attributes. For any of
 
those two reasons, <code>Custom</code> objects can participate in cycles:
 
  
 
<div class="highlight-pycon notranslate">
 
<div class="highlight-pycon notranslate">
第1,363行: 第1,185行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; import custom3
+
<syntaxhighlight lang="pycon">>>> import custom3
&gt;&gt;&gt; class Derived(custom3.Custom): pass
+
>>> class Derived(custom3.Custom): pass
 
...
 
...
&gt;&gt;&gt; n = Derived()
+
>>> n = Derived()
&gt;&gt;&gt; n.some_attribute = n</pre>
+
>>> n.some_attribute = n</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
To allow a <code>Custom</code> instance participating in a reference cycle to
+
为了允许循环 GC 正确检测和收集参与引用循环的 <code>Custom</code> 实例,我们的 <code>Custom</code> 类型需要填充两个额外的插槽并启用启用这些插槽的标志:
be properly detected and collected by the cyclic GC, our <code>Custom</code> type
 
needs to fill two additional slots and to enable a flag that enables these slots:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,380行: 第1,200行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>#define PY_SSIZE_T_CLEAN
+
<syntaxhighlight lang="c">#define PY_SSIZE_T_CLEAN
#include &lt;Python.h&gt;
+
#include <Python.h>
#include &quot;structmember.h&quot;
+
#include "structmember.h"
  
 
typedef struct {
 
typedef struct {
第1,394行: 第1,214行:
 
Custom_traverse(CustomObject *self, visitproc visit, void *arg)
 
Custom_traverse(CustomObject *self, visitproc visit, void *arg)
 
{
 
{
     Py_VISIT(self-&gt;first);
+
     Py_VISIT(self->first);
     Py_VISIT(self-&gt;last);
+
     Py_VISIT(self->last);
 
     return 0;
 
     return 0;
 
}
 
}
第1,402行: 第1,222行:
 
Custom_clear(CustomObject *self)
 
Custom_clear(CustomObject *self)
 
{
 
{
     Py_CLEAR(self-&gt;first);
+
     Py_CLEAR(self->first);
     Py_CLEAR(self-&gt;last);
+
     Py_CLEAR(self->last);
 
     return 0;
 
     return 0;
 
}
 
}
第1,412行: 第1,232行:
 
     PyObject_GC_UnTrack(self);
 
     PyObject_GC_UnTrack(self);
 
     Custom_clear(self);
 
     Custom_clear(self);
     Py_TYPE(self)-&gt;tp_free((PyObject *) self);
+
     Py_TYPE(self)->tp_free((PyObject *) self);
 
}
 
}
  
第1,419行: 第1,239行:
 
{
 
{
 
     CustomObject *self;
 
     CustomObject *self;
     self = (CustomObject *) type-&gt;tp_alloc(type, 0);
+
     self = (CustomObject *) type->tp_alloc(type, 0);
 
     if (self != NULL) {
 
     if (self != NULL) {
         self-&gt;first = PyUnicode_FromString(&quot;&quot;);
+
         self->first = PyUnicode_FromString("");
         if (self-&gt;first == NULL) {
+
         if (self->first == NULL) {
 
             Py_DECREF(self);
 
             Py_DECREF(self);
 
             return NULL;
 
             return NULL;
 
         }
 
         }
         self-&gt;last = PyUnicode_FromString(&quot;&quot;);
+
         self->last = PyUnicode_FromString("");
         if (self-&gt;last == NULL) {
+
         if (self->last == NULL) {
 
             Py_DECREF(self);
 
             Py_DECREF(self);
 
             return NULL;
 
             return NULL;
 
         }
 
         }
         self-&gt;number = 0;
+
         self->number = 0;
 
     }
 
     }
 
     return (PyObject *) self;
 
     return (PyObject *) self;
第1,439行: 第1,259行:
 
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
 
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
 
{
 
{
     static char *kwlist[] = {&quot;first&quot;, &quot;last&quot;, &quot;number&quot;, NULL};
+
     static char *kwlist[] = {"first", "last", "number", NULL};
 
     PyObject *first = NULL, *last = NULL, *tmp;
 
     PyObject *first = NULL, *last = NULL, *tmp;
  
     if (!PyArg_ParseTupleAndKeywords(args, kwds, &quot;|UUi&quot;, kwlist,
+
     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
                                     &amp;first, &amp;last,
+
                                     &first, &last,
                                     &amp;self-&gt;number))
+
                                     &self->number))
 
         return -1;
 
         return -1;
  
 
     if (first) {
 
     if (first) {
         tmp = self-&gt;first;
+
         tmp = self->first;
 
         Py_INCREF(first);
 
         Py_INCREF(first);
         self-&gt;first = first;
+
         self->first = first;
 
         Py_DECREF(tmp);
 
         Py_DECREF(tmp);
 
     }
 
     }
 
     if (last) {
 
     if (last) {
         tmp = self-&gt;last;
+
         tmp = self->last;
 
         Py_INCREF(last);
 
         Py_INCREF(last);
         self-&gt;last = last;
+
         self->last = last;
 
         Py_DECREF(tmp);
 
         Py_DECREF(tmp);
 
     }
 
     }
第1,463行: 第1,283行:
  
 
static PyMemberDef Custom_members[] = {
 
static PyMemberDef Custom_members[] = {
     {&quot;number&quot;, T_INT, offsetof(CustomObject, number), 0,
+
     {"number", T_INT, offsetof(CustomObject, number), 0,
     &quot;custom number&quot;},
+
     "custom number"},
 
     {NULL}  /* Sentinel */
 
     {NULL}  /* Sentinel */
 
};
 
};
第1,471行: 第1,291行:
 
Custom_getfirst(CustomObject *self, void *closure)
 
Custom_getfirst(CustomObject *self, void *closure)
 
{
 
{
     Py_INCREF(self-&gt;first);
+
     Py_INCREF(self->first);
     return self-&gt;first;
+
     return self->first;
 
}
 
}
  
第1,479行: 第1,299行:
 
{
 
{
 
     if (value == NULL) {
 
     if (value == NULL) {
         PyErr_SetString(PyExc_TypeError, &quot;Cannot delete the first attribute&quot;);
+
         PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
 
         return -1;
 
         return -1;
 
     }
 
     }
 
     if (!PyUnicode_Check(value)) {
 
     if (!PyUnicode_Check(value)) {
 
         PyErr_SetString(PyExc_TypeError,
 
         PyErr_SetString(PyExc_TypeError,
                         &quot;The first attribute value must be a string&quot;);
+
                         "The first attribute value must be a string");
 
         return -1;
 
         return -1;
 
     }
 
     }
 
     Py_INCREF(value);
 
     Py_INCREF(value);
     Py_CLEAR(self-&gt;first);
+
     Py_CLEAR(self->first);
     self-&gt;first = value;
+
     self->first = value;
 
     return 0;
 
     return 0;
 
}
 
}
第1,496行: 第1,316行:
 
Custom_getlast(CustomObject *self, void *closure)
 
Custom_getlast(CustomObject *self, void *closure)
 
{
 
{
     Py_INCREF(self-&gt;last);
+
     Py_INCREF(self->last);
     return self-&gt;last;
+
     return self->last;
 
}
 
}
  
第1,504行: 第1,324行:
 
{
 
{
 
     if (value == NULL) {
 
     if (value == NULL) {
         PyErr_SetString(PyExc_TypeError, &quot;Cannot delete the last attribute&quot;);
+
         PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
 
         return -1;
 
         return -1;
 
     }
 
     }
 
     if (!PyUnicode_Check(value)) {
 
     if (!PyUnicode_Check(value)) {
 
         PyErr_SetString(PyExc_TypeError,
 
         PyErr_SetString(PyExc_TypeError,
                         &quot;The last attribute value must be a string&quot;);
+
                         "The last attribute value must be a string");
 
         return -1;
 
         return -1;
 
     }
 
     }
 
     Py_INCREF(value);
 
     Py_INCREF(value);
     Py_CLEAR(self-&gt;last);
+
     Py_CLEAR(self->last);
     self-&gt;last = value;
+
     self->last = value;
 
     return 0;
 
     return 0;
 
}
 
}
  
 
static PyGetSetDef Custom_getsetters[] = {
 
static PyGetSetDef Custom_getsetters[] = {
     {&quot;first&quot;, (getter) Custom_getfirst, (setter) Custom_setfirst,
+
     {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
     &quot;first name&quot;, NULL},
+
     "first name", NULL},
     {&quot;last&quot;, (getter) Custom_getlast, (setter) Custom_setlast,
+
     {"last", (getter) Custom_getlast, (setter) Custom_setlast,
     &quot;last name&quot;, NULL},
+
     "last name", NULL},
 
     {NULL}  /* Sentinel */
 
     {NULL}  /* Sentinel */
 
};
 
};
第1,529行: 第1,349行:
 
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
 
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
 
{
 
{
     return PyUnicode_FromFormat(&quot;%S %S&quot;, self-&gt;first, self-&gt;last);
+
     return PyUnicode_FromFormat("%S %S", self->first, self->last);
 
}
 
}
  
 
static PyMethodDef Custom_methods[] = {
 
static PyMethodDef Custom_methods[] = {
     {&quot;name&quot;, (PyCFunction) Custom_name, METH_NOARGS,
+
     {"name", (PyCFunction) Custom_name, METH_NOARGS,
     &quot;Return the name, combining the first and last name&quot;
+
     "Return the name, combining the first and last name"
 
     },
 
     },
 
     {NULL}  /* Sentinel */
 
     {NULL}  /* Sentinel */
第1,541行: 第1,361行:
 
static PyTypeObject CustomType = {
 
static PyTypeObject CustomType = {
 
     PyVarObject_HEAD_INIT(NULL, 0)
 
     PyVarObject_HEAD_INIT(NULL, 0)
     .tp_name = &quot;custom4.Custom&quot;,
+
     .tp_name = "custom4.Custom",
     .tp_doc = &quot;Custom objects&quot;,
+
     .tp_doc = "Custom objects",
 
     .tp_basicsize = sizeof(CustomObject),
 
     .tp_basicsize = sizeof(CustomObject),
 
     .tp_itemsize = 0,
 
     .tp_itemsize = 0,
第1,558行: 第1,378行:
 
static PyModuleDef custommodule = {
 
static PyModuleDef custommodule = {
 
     PyModuleDef_HEAD_INIT,
 
     PyModuleDef_HEAD_INIT,
     .m_name = &quot;custom4&quot;,
+
     .m_name = "custom4",
     .m_doc = &quot;Example module that creates an extension type.&quot;,
+
     .m_doc = "Example module that creates an extension type.",
 
     .m_size = -1,
 
     .m_size = -1,
 
};
 
};
第1,567行: 第1,387行:
 
{
 
{
 
     PyObject *m;
 
     PyObject *m;
     if (PyType_Ready(&amp;CustomType) &lt; 0)
+
     if (PyType_Ready(&CustomType) < 0)
 
         return NULL;
 
         return NULL;
  
     m = PyModule_Create(&amp;custommodule);
+
     m = PyModule_Create(&custommodule);
 
     if (m == NULL)
 
     if (m == NULL)
 
         return NULL;
 
         return NULL;
  
     Py_INCREF(&amp;CustomType);
+
     Py_INCREF(&CustomType);
     if (PyModule_AddObject(m, &quot;Custom&quot;, (PyObject *) &amp;CustomType) &lt; 0) {
+
     if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
         Py_DECREF(&amp;CustomType);
+
         Py_DECREF(&CustomType);
 
         Py_DECREF(m);
 
         Py_DECREF(m);
 
         return NULL;
 
         return NULL;
第1,582行: 第1,402行:
  
 
     return m;
 
     return m;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
First, the traversal method lets the cyclic GC know about subobjects that could
+
首先,遍历方法让循环 GC 知道可以参与循环的子对象:
participate in cycles:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,594行: 第1,413行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static int
+
<syntaxhighlight lang="c">static int
 
Custom_traverse(CustomObject *self, visitproc visit, void *arg)
 
Custom_traverse(CustomObject *self, visitproc visit, void *arg)
 
{
 
{
 
     int vret;
 
     int vret;
     if (self-&gt;first) {
+
     if (self->first) {
         vret = visit(self-&gt;first, arg);
+
         vret = visit(self->first, arg);
 
         if (vret != 0)
 
         if (vret != 0)
 
             return vret;
 
             return vret;
 
     }
 
     }
     if (self-&gt;last) {
+
     if (self->last) {
         vret = visit(self-&gt;last, arg);
+
         vret = visit(self->last, arg);
 
         if (vret != 0)
 
         if (vret != 0)
 
             return vret;
 
             return vret;
 
     }
 
     }
 
     return 0;
 
     return 0;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
For each subobject that can participate in cycles, we need to call the
+
对于每个可以参与循环的子对象,我们需要调用<code>visit()</code>函数,该函数传递给遍历方法。 <code>visit()</code> 函数将子对象和传递给遍历方法的额外参数 ''arg'' 作为参数。 它返回一个整数值,如果它不为零,则必须返回该值。
<code>visit()</code> function, which is passed to the traversal method. The
 
<code>visit()</code> function takes as arguments the subobject and the extra argument
 
''arg'' passed to the traversal method. It returns an integer value that must be
 
returned if it is non-zero.
 
  
Python provides a [[../../c-api/gcsupport#c|<code>Py_VISIT()</code>]] macro that automates calling visit
+
Python 提供了一个 [[../../c-api/gcsupport#c|Py_VISIT()]] 宏,可以自动调用访问函数。 使用 [[../../c-api/gcsupport#c|Py_VISIT()]],我们可以最小化 <code>Custom_traverse</code> 中的样板数量:
functions. With [[../../c-api/gcsupport#c|<code>Py_VISIT()</code>]], we can minimize the amount of boilerplate
 
in <code>Custom_traverse</code>:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,628行: 第1,441行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static int
+
<syntaxhighlight lang="c">static int
 
Custom_traverse(CustomObject *self, visitproc visit, void *arg)
 
Custom_traverse(CustomObject *self, visitproc visit, void *arg)
 
{
 
{
     Py_VISIT(self-&gt;first);
+
     Py_VISIT(self->first);
     Py_VISIT(self-&gt;last);
+
     Py_VISIT(self->last);
 
     return 0;
 
     return 0;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
第1,641行: 第1,454行:
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
The [[../../c-api/typeobj#c.PyTypeObject|<code>tp_traverse</code>]] implementation must name its
+
[[../../c-api/typeobj#c.PyTypeObject|tp_traverse]] 实现必须准确命名其参数 ''visit'' ''arg'',以便使用 [[../../c-api/gcsupport#c|Py_VISIT()]]
arguments exactly ''visit'' and ''arg'' in order to use [[../../c-api/gcsupport#c|<code>Py_VISIT()</code>]].
 
  
  
 
</div>
 
</div>
Second, we need to provide a method for clearing any subobjects that can
+
其次,我们需要提供一种清除任何可以参与循环的子对象的方法:
participate in cycles:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,655行: 第1,466行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static int
+
<syntaxhighlight lang="c">static int
 
Custom_clear(CustomObject *self)
 
Custom_clear(CustomObject *self)
 
{
 
{
     Py_CLEAR(self-&gt;first);
+
     Py_CLEAR(self->first);
     Py_CLEAR(self-&gt;last);
+
     Py_CLEAR(self->last);
 
     return 0;
 
     return 0;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Notice the use of the [[../../c-api/refcounting#c|<code>Py_CLEAR()</code>]] macro. It is the recommended and safe
+
注意 [[../../c-api/refcounting#c|Py_CLEAR()]] 宏的使用。 这是在减少引用计数的同时清除任意类型数据属性的推荐且安全的方法。 如果您在将属性设置为 <code>NULL</code> 之前调用 [[../../c-api/refcounting#c|Py_XDECREF()]],则该属性的析构函数可能会回调到再次读取该属性的代码([X212X ] 尤其是 如果有参考循环)。
way to clear data attributes of arbitrary types while decrementing
 
their reference counts. If you were to call [[../../c-api/refcounting#c|<code>Py_XDECREF()</code>]] instead
 
on the attribute before setting it to <code>NULL</code>, there is a possibility
 
that the attribute's destructor would call back into code that reads the
 
attribute again (''especially'' if there is a reference cycle).
 
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
You could emulate [[../../c-api/refcounting#c|<code>Py_CLEAR()</code>]] by writing:
+
您可以通过编写来模拟 [[../../c-api/refcounting#c|Py_CLEAR()]]
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,683行: 第1,489行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>PyObject *tmp;
+
<syntaxhighlight lang="c">PyObject *tmp;
tmp = self-&gt;first;
+
tmp = self->first;
self-&gt;first = NULL;
+
self->first = NULL;
Py_XDECREF(tmp);</pre>
+
Py_XDECREF(tmp);</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Nevertheless, it is much easier and less error-prone to always
+
尽管如此,在删除属性时始终使用 [[../../c-api/refcounting#c|Py_CLEAR()]] 更容易且不易出错。 不要试图以牺牲健壮性为代价进行微观优化!
use [[../../c-api/refcounting#c|<code>Py_CLEAR()</code>]] when deleting an attribute. Don't
 
try to micro-optimize at the expense of robustness!
 
  
  
 
</div>
 
</div>
The deallocator <code>Custom_dealloc</code> may call arbitrary code when clearing
+
清除属性时,deallocator <code>Custom_dealloc</code> 可能会调用任意代码。 这意味着可以在函数内部触发循环 GC。 由于 GC 假定引用计数不为零,因此我们需要在清除成员之前通过调用 [[../../c-api/gcsupport#c|PyObject_GC_UnTrack()]] 来从 GC 中取消跟踪对象。 这是我们使用 [[../../c-api/gcsupport#c|PyObject_GC_UnTrack()]] <code>Custom_clear</code> 重新实现的释放器:
attributes. It means the circular GC can be triggered inside the function.
 
Since the GC assumes reference count is not zero, we need to untrack the object
 
from the GC by calling [[../../c-api/gcsupport#c|<code>PyObject_GC_UnTrack()</code>]] before clearing members.
 
Here is our reimplemented deallocator using [[../../c-api/gcsupport#c|<code>PyObject_GC_UnTrack()</code>]]
 
and <code>Custom_clear</code>:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,708行: 第1,507行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static void
+
<syntaxhighlight lang="c">static void
 
Custom_dealloc(CustomObject *self)
 
Custom_dealloc(CustomObject *self)
 
{
 
{
 
     PyObject_GC_UnTrack(self);
 
     PyObject_GC_UnTrack(self);
 
     Custom_clear(self);
 
     Custom_clear(self);
     Py_TYPE(self)-&gt;tp_free((PyObject *) self);
+
     Py_TYPE(self)->tp_free((PyObject *) self);
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Finally, we add the [[../../c-api/typeobj#Py_TPFLAGS_HAVE_GC|<code>Py_TPFLAGS_HAVE_GC</code>]] flag to the class flags:
+
最后,我们将 [[../../c-api/typeobj#Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_HAVE_GC]] 标志添加到类标志中:
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,725行: 第1,524行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,</pre>
+
<syntaxhighlight lang="c">.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
That's pretty much it. If we had written custom [[../../c-api/typeobj#c.PyTypeObject|<code>tp_alloc</code>]] or
+
差不多就是这样。 如果我们编写了自定义的 [[../../c-api/typeobj#c.PyTypeObject|tp_alloc]] [[../../c-api/typeobj#c.PyTypeObject|tp_free]] 处理程序,我们需要修改它们以进行循环垃圾收集。 大多数扩展将使用自动提供的版本。
[[../../c-api/typeobj#c.PyTypeObject|<code>tp_free</code>]] handlers, we'd need to modify them for cyclic
 
garbage collection. Most extensions will use the versions automatically provided.
 
  
  
第1,738行: 第1,535行:
 
<div id="subclassing-other-types" class="section">
 
<div id="subclassing-other-types" class="section">
  
== <span class="section-number">2.5. </span>Subclassing other types ==
+
== 2.5. 子类化其他类型 ==
  
It is possible to create new extension types that are derived from existing
+
可以创建从现有类型派生的新扩展类型。 从内置类型继承是最容易的,因为扩展可以轻松使用它需要的 [[../../c-api/type#c|PyTypeObject]]。 在扩展模块之间共享这些 [[../../c-api/type#c|PyTypeObject]] 结构可能很困难。
types. It is easiest to inherit from the built in types, since an extension can
 
easily use the [[../../c-api/type#c|<code>PyTypeObject</code>]] it needs. It can be difficult to share
 
these [[../../c-api/type#c|<code>PyTypeObject</code>]] structures between extension modules.
 
  
In this example we will create a <code>SubList</code> type that inherits from the
+
在这个例子中,我们将创建一个从内置 [[../../library/stdtypes#list|list]] 类型继承的 <code>SubList</code> 类型。 新类型将与常规列表完全兼容,但会有一个额外的 <code>increment()</code> 方法来增加内部计数器:
built-in [[../../library/stdtypes#list|<code>list</code>]] type. The new type will be completely compatible with
 
regular lists, but will have an additional <code>increment()</code> method that
 
increases an internal counter:
 
  
 
<div class="highlight-pycon notranslate">
 
<div class="highlight-pycon notranslate">
第1,754行: 第1,545行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; import sublist
+
<syntaxhighlight lang="pycon">>>> import sublist
&gt;&gt;&gt; s = sublist.SubList(range(3))
+
>>> s = sublist.SubList(range(3))
&gt;&gt;&gt; s.extend(s)
+
>>> s.extend(s)
&gt;&gt;&gt; print(len(s))
+
>>> print(len(s))
 
6
 
6
&gt;&gt;&gt; print(s.increment())
+
>>> print(s.increment())
 
1
 
1
&gt;&gt;&gt; print(s.increment())
+
>>> print(s.increment())
2</pre>
+
2</syntaxhighlight>
  
 
</div>
 
</div>
第1,771行: 第1,562行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>#define PY_SSIZE_T_CLEAN
+
<syntaxhighlight lang="c">#define PY_SSIZE_T_CLEAN
#include &lt;Python.h&gt;
+
#include <Python.h>
  
 
typedef struct {
 
typedef struct {
第1,782行: 第1,573行:
 
SubList_increment(SubListObject *self, PyObject *unused)
 
SubList_increment(SubListObject *self, PyObject *unused)
 
{
 
{
     self-&gt;state++;
+
     self->state++;
     return PyLong_FromLong(self-&gt;state);
+
     return PyLong_FromLong(self->state);
 
}
 
}
  
 
static PyMethodDef SubList_methods[] = {
 
static PyMethodDef SubList_methods[] = {
     {&quot;increment&quot;, (PyCFunction) SubList_increment, METH_NOARGS,
+
     {"increment", (PyCFunction) SubList_increment, METH_NOARGS,
     PyDoc_STR(&quot;increment state counter&quot;)},
+
     PyDoc_STR("increment state counter")},
 
     {NULL},
 
     {NULL},
 
};
 
};
第1,795行: 第1,586行:
 
SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
 
SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
 
{
 
{
     if (PyList_Type.tp_init((PyObject *) self, args, kwds) &lt; 0)
+
     if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
 
         return -1;
 
         return -1;
     self-&gt;state = 0;
+
     self->state = 0;
 
     return 0;
 
     return 0;
 
}
 
}
第1,803行: 第1,594行:
 
static PyTypeObject SubListType = {
 
static PyTypeObject SubListType = {
 
     PyVarObject_HEAD_INIT(NULL, 0)
 
     PyVarObject_HEAD_INIT(NULL, 0)
     .tp_name = &quot;sublist.SubList&quot;,
+
     .tp_name = "sublist.SubList",
     .tp_doc = &quot;SubList objects&quot;,
+
     .tp_doc = "SubList objects",
 
     .tp_basicsize = sizeof(SubListObject),
 
     .tp_basicsize = sizeof(SubListObject),
 
     .tp_itemsize = 0,
 
     .tp_itemsize = 0,
第1,814行: 第1,605行:
 
static PyModuleDef sublistmodule = {
 
static PyModuleDef sublistmodule = {
 
     PyModuleDef_HEAD_INIT,
 
     PyModuleDef_HEAD_INIT,
     .m_name = &quot;sublist&quot;,
+
     .m_name = "sublist",
     .m_doc = &quot;Example module that creates an extension type.&quot;,
+
     .m_doc = "Example module that creates an extension type.",
 
     .m_size = -1,
 
     .m_size = -1,
 
};
 
};
第1,823行: 第1,614行:
 
{
 
{
 
     PyObject *m;
 
     PyObject *m;
     SubListType.tp_base = &amp;PyList_Type;
+
     SubListType.tp_base = &PyList_Type;
     if (PyType_Ready(&amp;SubListType) &lt; 0)
+
     if (PyType_Ready(&SubListType) < 0)
 
         return NULL;
 
         return NULL;
  
     m = PyModule_Create(&amp;sublistmodule);
+
     m = PyModule_Create(&sublistmodule);
 
     if (m == NULL)
 
     if (m == NULL)
 
         return NULL;
 
         return NULL;
  
     Py_INCREF(&amp;SubListType);
+
     Py_INCREF(&SubListType);
     if (PyModule_AddObject(m, &quot;SubList&quot;, (PyObject *) &amp;SubListType) &lt; 0) {
+
     if (PyModule_AddObject(m, "SubList", (PyObject *) &SubListType) < 0) {
         Py_DECREF(&amp;SubListType);
+
         Py_DECREF(&SubListType);
 
         Py_DECREF(m);
 
         Py_DECREF(m);
 
         return NULL;
 
         return NULL;
第1,839行: 第1,630行:
  
 
     return m;
 
     return m;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
As you can see, the source code closely resembles the <code>Custom</code> examples in
+
如您所见,源代码与前几节中的 <code>Custom</code> 示例非常相似。 我们将分解它们之间的主要区别。
previous sections. We will break down the main differences between them.
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,851行: 第1,641行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>typedef struct {
+
<syntaxhighlight lang="c">typedef struct {
 
     PyListObject list;
 
     PyListObject list;
 
     int state;
 
     int state;
} SubListObject;</pre>
+
} SubListObject;</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The primary difference for derived type objects is that the base type's
+
派生类型对象的主要区别在于基类型的对象结构必须是第一个值。 基本类型将在其结构的开头包含 [[../../c-api/structures#c|PyObject_HEAD()]]
object structure must be the first value. The base type will already include
 
the [[../../c-api/structures#c|<code>PyObject_HEAD()</code>]] at the beginning of its structure.
 
  
When a Python object is a <code>SubList</code> instance, its <code>PyObject *</code> pointer
+
Python 对象是 <code>SubList</code> 实例时,其 <code>PyObject *</code> 指针可以安全地转换为 <code>PyListObject *</code> <code>SubListObject *</code>
can be safely cast to both <code>PyListObject *</code> and <code>SubListObject *</code>:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,870行: 第1,657行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>static int
+
<syntaxhighlight lang="c">static int
 
SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
 
SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
 
{
 
{
     if (PyList_Type.tp_init((PyObject *) self, args, kwds) &lt; 0)
+
     if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
 
         return -1;
 
         return -1;
     self-&gt;state = 0;
+
     self->state = 0;
 
     return 0;
 
     return 0;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
We see above how to call through to the <code>__init__</code> method of the base
+
我们在上面看到如何调用基本类型的 <code>__init__</code> 方法。
type.
 
  
This pattern is important when writing a type with custom
+
在编写具有自定义 [[../../c-api/typeobj#c.PyTypeObject|tp_new]] [[../../c-api/typeobj#c.PyTypeObject|tp_dealloc]] 成员的类型时,此模式很重要。 [[../../c-api/typeobj#c.PyTypeObject|tp_new]] 处理程序实际上不应该用它的 [[../../c-api/typeobj#c.PyTypeObject|tp_alloc]] 为对象创建内存,而是让基类通过调用它自己的 [[../../c-api/typeobj#c.PyTypeObject|tp_new]] 来处理它。
[[../../c-api/typeobj#c.PyTypeObject|<code>tp_new</code>]] and [[../../c-api/typeobj#c.PyTypeObject|<code>tp_dealloc</code>]]
 
members. The [[../../c-api/typeobj#c.PyTypeObject|<code>tp_new</code>]] handler should not actually
 
create the memory for the object with its [[../../c-api/typeobj#c.PyTypeObject|<code>tp_alloc</code>]],
 
but let the base class handle it by calling its own [[../../c-api/typeobj#c.PyTypeObject|<code>tp_new</code>]].
 
  
The [[../../c-api/type#c|<code>PyTypeObject</code>]] struct supports a [[../../c-api/typeobj#c.PyTypeObject|<code>tp_base</code>]]
+
[[../../c-api/type#c|PyTypeObject]] 结构支持 [[../../c-api/typeobj#c.PyTypeObject|tp_base]] 指定类型的具体基类。 由于跨平台编译器问题,您不能直接使用对 [[../../c-api/list#c|PyList_Type]] 的引用来填充该字段; 它应该稍后在模块初始化函数中完成:
specifying the type's concrete base class. Due to cross-platform compiler
 
issues, you can't fill that field directly with a reference to
 
[[../../c-api/list#c|<code>PyList_Type</code>]]; it should be done later in the module initialization
 
function:
 
  
 
<div class="highlight-c notranslate">
 
<div class="highlight-c notranslate">
第1,901行: 第1,679行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>PyMODINIT_FUNC
+
<syntaxhighlight lang="c">PyMODINIT_FUNC
 
PyInit_sublist(void)
 
PyInit_sublist(void)
 
{
 
{
 
     PyObject* m;
 
     PyObject* m;
     SubListType.tp_base = &amp;PyList_Type;
+
     SubListType.tp_base = &PyList_Type;
     if (PyType_Ready(&amp;SubListType) &lt; 0)
+
     if (PyType_Ready(&SubListType) < 0)
 
         return NULL;
 
         return NULL;
  
     m = PyModule_Create(&amp;sublistmodule);
+
     m = PyModule_Create(&sublistmodule);
 
     if (m == NULL)
 
     if (m == NULL)
 
         return NULL;
 
         return NULL;
  
     Py_INCREF(&amp;SubListType);
+
     Py_INCREF(&SubListType);
     if (PyModule_AddObject(m, &quot;SubList&quot;, (PyObject *) &amp;SubListType) &lt; 0) {
+
     if (PyModule_AddObject(m, "SubList", (PyObject *) &SubListType) < 0) {
         Py_DECREF(&amp;SubListType);
+
         Py_DECREF(&SubListType);
 
         Py_DECREF(m);
 
         Py_DECREF(m);
 
         return NULL;
 
         return NULL;
第1,921行: 第1,699行:
  
 
     return m;
 
     return m;
}</pre>
+
}</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Before calling [[../../c-api/type#c|<code>PyType_Ready()</code>]], the type structure must have the
+
在调用 [[../../c-api/type#c|PyType_Ready()]] 之前,类型结构必须填充 [[../../c-api/typeobj#c.PyTypeObject|tp_base]] 槽。 当我们派生一个现有类型时,没有必要用 [[../../c-api/type#c|PyType_GenericNew()]] 填充 [[../../c-api/typeobj#c.PyTypeObject|tp_alloc]] 槽——将继承基类型的分配函数。
[[../../c-api/typeobj#c.PyTypeObject|<code>tp_base</code>]] slot filled in. When we are deriving an
 
existing type, it is not necessary to fill out the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_alloc</code>]]
 
slot with [[../../c-api/type#c|<code>PyType_GenericNew()</code>]] -- the allocation function from the base
 
type will be inherited.
 
  
After that, calling [[../../c-api/type#c|<code>PyType_Ready()</code>]] and adding the type object to the
+
之后,调用 [[../../c-api/type#c|PyType_Ready()]] 并将类型对象添加到模块与基本的 <code>Custom</code> 示例相同。
module is the same as with the basic <code>Custom</code> examples.
 
  
Footnotes
+
脚注
  
 
; <span class="brackets">[[#id1|1]]</span>
 
; <span class="brackets">[[#id1|1]]</span>
: This is true when we know that the object is a basic type, like a string or a float.
+
: 当我们知道对象是一个基本类型时,这是真的,比如字符串或浮点数。
 
; <span class="brackets">[[#id2|2]]</span>
 
; <span class="brackets">[[#id2|2]]</span>
: We relied on this in the [[../../c-api/typeobj#c.PyTypeObject|<code>tp_dealloc</code>]] handler in this example, because our type doesn't support garbage collection.
+
: 在本例中,我们在 [[../../c-api/typeobj#c.PyTypeObject|tp_dealloc]] 处理程序中依赖于此,因为我们的类型不支持垃圾收集。
 
; <span class="brackets">[[#id3|3]]</span>
 
; <span class="brackets">[[#id3|3]]</span>
: We now know that the first and last members are strings, so perhaps we could be less careful about decrementing their reference counts, however, we accept instances of string subclasses. Even though deallocating normal strings won't call back into our objects, we can't guarantee that deallocating an instance of a string subclass won't call back into our objects.
+
: 我们现在知道第一个和最后一个成员是字符串,所以也许我们可以在减少它们的引用计数时不那么小心,但是,我们接受字符串子类的实例。 即使释放普通字符串不会回调到我们的对象中,我们也不能保证释放字符串子类的实例不会回调到我们的对象中。
 
; <span class="brackets">[[#id4|4]]</span>
 
; <span class="brackets">[[#id4|4]]</span>
: Also, even with our attributes restricted to strings instances, the user could pass arbitrary [[../../library/stdtypes#str|<code>str</code>]] subclasses and therefore still create reference cycles.
+
: 此外,即使我们的属性仅限于字符串实例,用户也可以传递任意 [[../../library/stdtypes#str|str]] 子类,因此仍然创建引用循环。
  
  
第1,950行: 第1,723行:
  
 
</div>
 
</div>
 +
<div class="clearer">
  
[[Category:Python 3.9 中文文档]]
+
 
 +
 
 +
</div>
 +
 
 +
[[Category:Python 3.9 文档]]

2021年10月31日 (日) 04:50的最新版本

2. 定义扩展类型:教程

Python 允许 C 扩展模块的编写者定义可以从 Python 代码操作的新类型,很像内置的 strlist 类型。 所有扩展类型的代码都遵循一个模式,但在开始之前,您需要了解一些细节。 本文档是对该主题的温和介绍。

2.1. 基础知识

CPython 运行时将所有 Python 对象视为 PyObject* 类型的变量,作为所有 Python 对象的“基本类型”。 PyObject 结构本身只包含对象的 引用计数 和一个指向对象“类型对象”的指针。 这就是行动所在; 类型对象确定解释器调用哪些 (C) 函数,例如,在对象上查找属性、调用方法或将其与另一个对象相乘。 这些 C 函数称为“类型方法”。

所以,如果你想定义一个新的扩展类型,你需要创建一个新的类型对象。

这种事情只能通过例子来解释,所以这里有一个最小但完整的模块,它在 C 扩展模块 custom 中定义了一个名为 Custom 的新类型:

笔记

我们在这里展示的是定义 static 扩展类型的传统方法。 它应该足以满足大多数用途。 C API 还允许使用 PyType_FromSpec() 函数定义堆分配的扩展类型,本教程未涵盖该函数。


#define PY_SSIZE_T_CLEAN
#include <Python.h>

typedef struct {
    PyObject_HEAD
    /* Type-specific fields go here. */
} CustomObject;

static PyTypeObject CustomType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "custom.Custom",
    .tp_doc = "Custom objects",
    .tp_basicsize = sizeof(CustomObject),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT,
    .tp_new = PyType_GenericNew,
};

static PyModuleDef custommodule = {
    PyModuleDef_HEAD_INIT,
    .m_name = "custom",
    .m_doc = "Example module that creates an extension type.",
    .m_size = -1,
};

PyMODINIT_FUNC
PyInit_custom(void)
{
    PyObject *m;
    if (PyType_Ready(&CustomType) < 0)
        return NULL;

    m = PyModule_Create(&custommodule);
    if (m == NULL)
        return NULL;

    Py_INCREF(&CustomType);
    if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
        Py_DECREF(&CustomType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

现在一下子就明白了很多,但希望上一章的内容看起来很熟悉。 这个文件定义了三件事:

  1. Custom object 包含什么:这是 CustomObject 结构体,它为每个 Custom 实例分配一次。
  2. Custom type 的行为方式:这是 CustomType 结构体,它定义了解释器在请求特定操作时检查的一组标志和函数指针。
  3. 如何初始化 custom 模块:这是 PyInit_custom 函数和关联的 custommodule 结构。

第一点是:

typedef struct {
    PyObject_HEAD
} CustomObject;

这是自定义对象将包含的内容。 PyObject_HEAD 在每个对象结构的开头是强制性的,它定义了一个名为 ob_base 的字段,类型为 PyObject,包含一个指向类型对象的指针和一个引用计数(这些可以分别使用宏 Py_REFCNTPy_TYPE 访问)。 使用宏的原因是抽象布局并在调试版本中启用其他字段。

笔记

PyObject_HEAD 宏后面没有分号。 小心添加一个意外:一些编译器会抱怨。


当然,除了标准的 PyObject_HEAD 样板之外,对象通常还存储额外的数据; 例如,这里是标准 Python 浮点数的定义:

typedef struct {
    PyObject_HEAD
    double ob_fval;
} PyFloatObject;

第二位是类型对象的定义。

static PyTypeObject CustomType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "custom.Custom",
    .tp_doc = "Custom objects",
    .tp_basicsize = sizeof(CustomObject),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT,
    .tp_new = PyType_GenericNew,
};

笔记

我们建议使用上述 C99 风格的指定初始值设定项,以避免列出您不关心的所有 PyTypeObject 字段,也避免关心字段的声明顺序。


object.hPyTypeObject的实际定义比上面的定义有更多的字段。 其余字段将由 C 编译器用零填充,除非您需要它们,否则通常不显式指定它们。

我们将把它分开,一次一个领域:

PyVarObject_HEAD_INIT(NULL, 0)

此行是初始化上述 ob_base 字段的强制性样板。

.tp_name = "custom.Custom",

我们类型的名称。 这将出现在我们对象的默认文本表示和一些错误消息中,例如:

>>> "" + custom.Custom()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "custom.Custom") to str

请注意,名称是一个带点的名称,包括模块名称和模块内的类型名称。 本例中的模块为custom,类型为Custom,因此我们将类型名称设置为custom.Custom。 使用真正的虚线导入路径对于使您的类型与 pydocpickle 模块兼容很重要。

.tp_basicsize = sizeof(CustomObject),
.tp_itemsize = 0,

这是为了让 Python 在创建新的 Custom 实例时知道要分配多少内存。 tp_itemsize 仅用于可变大小的对象,否则应为零。

笔记

如果您希望您的类型可以从 Python 继承,并且您的类型与它的基类型具有相同的 tp_basicsize,那么您可能会遇到多重继承的问题。 您类型的 Python 子类必须首先在其 __bases__ 中列出您的类型,否则它将无法调用您的类型的 __new__() 方法而不出现错误。 您可以通过确保您的类型的 tp_basicsize 值大于其基本类型的值来避免此问题。 大多数情况下,无论如何这都是正确的,因为要么您的基类型将是 object,要么您将向基类型添加数据成员,从而增加其大小。


我们将类标志设置为 Py_TPFLAGS_DEFAULT

.tp_flags = Py_TPFLAGS_DEFAULT,

所有类型都应在其标志中包含此常量。 它启用至少在 Python 3.3 之前定义的所有成员。 如果您需要更多成员,则需要对相应的标志进行 OR 运算。

我们为 tp_doc 中的类型提供了一个文档字符串。

.tp_doc = "Custom objects",

要启用对象创建,我们必须提供 tp_new 处理程序。 这相当于 Python 方法 __new__(),但必须明确指定。 在这种情况下,我们可以使用 API 函数 PyType_GenericNew() 提供的默认实现。

.tp_new = PyType_GenericNew,

除了 PyInit_custom() 中的一些代码之外,文件中的其他所有内容都应该很熟悉:

if (PyType_Ready(&CustomType) < 0)
    return;

这将初始化 Custom 类型,将一些成员填充为适当的默认值,包括我们最初设置为 NULLob_type

Py_INCREF(&CustomType);
if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
    Py_DECREF(&CustomType);
    Py_DECREF(m);
    return NULL;
}

这会将类型添加到模块字典中。 这允许我们通过调用 Custom 类来创建 Custom 实例:

>>> import custom
>>> mycustom = custom.Custom()

就是这样! 剩下的就是建造它; 将上面的代码放在一个名为 custom.c 的文件中,然后:

from distutils.core import setup, Extension
setup(name="custom", version="1.0",
      ext_modules=[Extension("custom", ["custom.c"])])

在一个名为 setup.py 的文件中; 然后打字

$ python setup.py build

在 shell 中应该在子目录中生成一个文件 custom.so; 移至该目录并启动 Python — 您应该能够 import custom 并使用自定义对象。

那没那么难,是吗?

当然,当前的自定义类型非常无趣。 它没有数据,也不做任何事情。 它甚至不能被子类化。

笔记

虽然本文档展示了用于构建 C 扩展的标准 distutils 模块,但建议在实际用例中使用更新且维护更好的 setuptools 库。 有关如何执行此操作的文档超出了本文档的范围,可以在 Python 打包用户指南 中找到。


2.2. 向 Basic 示例添加数据和方法

让我们扩展基本示例以添加一些数据和方法。 让我们也使该类型可用作基类。 我们将创建一个新模块 custom2 来添加以下功能:

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"

typedef struct {
    PyObject_HEAD
    PyObject *first; /* first name */
    PyObject *last;  /* last name */
    int number;
} CustomObject;

static void
Custom_dealloc(CustomObject *self)
{
    Py_XDECREF(self->first);
    Py_XDECREF(self->last);
    Py_TYPE(self)->tp_free((PyObject *) self);
}

static PyObject *
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    CustomObject *self;
    self = (CustomObject *) type->tp_alloc(type, 0);
    if (self != NULL) {
        self->first = PyUnicode_FromString("");
        if (self->first == NULL) {
            Py_DECREF(self);
            return NULL;
        }
        self->last = PyUnicode_FromString("");
        if (self->last == NULL) {
            Py_DECREF(self);
            return NULL;
        }
        self->number = 0;
    }
    return (PyObject *) self;
}

static int
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwlist[] = {"first", "last", "number", NULL};
    PyObject *first = NULL, *last = NULL, *tmp;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
                                     &first, &last,
                                     &self->number))
        return -1;

    if (first) {
        tmp = self->first;
        Py_INCREF(first);
        self->first = first;
        Py_XDECREF(tmp);
    }
    if (last) {
        tmp = self->last;
        Py_INCREF(last);
        self->last = last;
        Py_XDECREF(tmp);
    }
    return 0;
}

static PyMemberDef Custom_members[] = {
    {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
     "first name"},
    {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,
     "last name"},
    {"number", T_INT, offsetof(CustomObject, number), 0,
     "custom number"},
    {NULL}  /* Sentinel */
};

static PyObject *
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
{
    if (self->first == NULL) {
        PyErr_SetString(PyExc_AttributeError, "first");
        return NULL;
    }
    if (self->last == NULL) {
        PyErr_SetString(PyExc_AttributeError, "last");
        return NULL;
    }
    return PyUnicode_FromFormat("%S %S", self->first, self->last);
}

static PyMethodDef Custom_methods[] = {
    {"name", (PyCFunction) Custom_name, METH_NOARGS,
     "Return the name, combining the first and last name"
    },
    {NULL}  /* Sentinel */
};

static PyTypeObject CustomType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "custom2.Custom",
    .tp_doc = "Custom objects",
    .tp_basicsize = sizeof(CustomObject),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
    .tp_new = Custom_new,
    .tp_init = (initproc) Custom_init,
    .tp_dealloc = (destructor) Custom_dealloc,
    .tp_members = Custom_members,
    .tp_methods = Custom_methods,
};

static PyModuleDef custommodule = {
    PyModuleDef_HEAD_INIT,
    .m_name = "custom2",
    .m_doc = "Example module that creates an extension type.",
    .m_size = -1,
};

PyMODINIT_FUNC
PyInit_custom2(void)
{
    PyObject *m;
    if (PyType_Ready(&CustomType) < 0)
        return NULL;

    m = PyModule_Create(&custommodule);
    if (m == NULL)
        return NULL;

    Py_INCREF(&CustomType);
    if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
        Py_DECREF(&CustomType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

此版本的模块有许多更改。

我们添加了一个额外的包括:

#include <structmember.h>

这包括提供我们用来处理属性的声明,如稍后所述。

Custom 类型现在在其 C 结构中具有三个数据属性,firstlastnumberfirstlast 变量是包含名字和姓氏的 Python 字符串。 number 属性是一个 C 整数。

对象结构会相应更新:

typedef struct {
    PyObject_HEAD
    PyObject *first; /* first name */
    PyObject *last;  /* last name */
    int number;
} CustomObject;

因为我们现在有数据要管理,所以我们必须更加小心地分配和释放对象。 至少,我们需要一个释放方法:

static void
Custom_dealloc(CustomObject *self)
{
    Py_XDECREF(self->first);
    Py_XDECREF(self->last);
    Py_TYPE(self)->tp_free((PyObject *) self);
}

分配给 tp_dealloc 成员:

.tp_dealloc = (destructor) Custom_dealloc,

此方法首先清除两个 Python 属性的引用计数。 Py_XDECREF() 正确处理其参数为 NULL 的情况(如果 tp_new 中途失败,可能会发生这种情况)。 然后它调用对象类型的 tp_free 成员(由 Py_TYPE(self) 计算)来释放对象的内存。 请注意,对象的类型可能不是 CustomType,因为该对象可能是子类的实例。

笔记

需要显式转换为上面的 destructor,因为我们定义了 Custom_dealloc 来接受一个 CustomObject * 参数,但是 tp_dealloc 函数指针期望接收一个 PyObject * 参数。 否则,编译器会发出警告。 这是面向对象的多态性,在 C 中!


我们要确保名字和姓氏被初始化为空字符串,因此我们提供了一个 tp_new 实现:

static PyObject *
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    CustomObject *self;
    self = (CustomObject *) type->tp_alloc(type, 0);
    if (self != NULL) {
        self->first = PyUnicode_FromString("");
        if (self->first == NULL) {
            Py_DECREF(self);
            return NULL;
        }
        self->last = PyUnicode_FromString("");
        if (self->last == NULL) {
            Py_DECREF(self);
            return NULL;
        }
        self->number = 0;
    }
    return (PyObject *) self;
}

并将其安装在 tp_new 成员中:

.tp_new = Custom_new,

tp_new 处理程序负责创建(而不是初始化)该类型的对象。 它在 Python 中公开为 __new__() 方法。 不需要定义 tp_new 成员,实际上许多扩展类型将简单地重用 PyType_GenericNew(),就像在上面的 Custom 类型的第一个版本中所做的那样。 在这种情况下,我们使用 tp_new 处理程序将 firstlast 属性初始化为非 NULL 默认值。

tp_new 被传递正在被实例化的类型(不一定是 CustomType,如果一个子类被实例化)和在调用该类型时传递的任何参数,并期望返回创建的实例。 tp_new 处理程序总是接受位置和关键字参数,但他们经常忽略这些参数,将参数处理留给初始化程序(又名 C 中的 tp_init 或 Python 中的 __init__)方法。

笔记

tp_new 不应显式调用 tp_init,因为解释器会自己调用。


tp_new 实现调用 tp_alloc 槽来分配内存:

self = (CustomObject *) type->tp_alloc(type, 0);

由于内存分配可能会失败,因此在继续之前,我们必须根据 NULL 检查 tp_alloc 结果。

笔记

我们没有自己填充 tp_alloc 插槽。 而是 PyType_Ready() 通过从我们的基类继承它来为我们填充它,默认情况下它是 object。 大多数类型使用默认分配策略。


笔记

如果您正在创建一个合作社 tp_new(一个调用基本类型的 tp_new__new__()),您必须 not 尝试确定在运行时使用方法解析顺序调用什么方法。 始终静态地确定您要调用的类型,并直接调用其 tp_new,或通过 type->tp_base->tp_new。 如果不这样做,您的类型的 Python 子类也从其他 Python 定义的类继承可能无法正常工作。 (具体来说,您可能无法在没有得到 TypeError 的情况下创建此类子类的实例。)


我们还定义了一个初始化函数,它接受参数来为我们的实例提供初始值:

static int
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwlist[] = {"first", "last", "number", NULL};
    PyObject *first = NULL, *last = NULL, *tmp;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
                                     &first, &last,
                                     &self->number))
        return -1;

    if (first) {
        tmp = self->first;
        Py_INCREF(first);
        self->first = first;
        Py_XDECREF(tmp);
    }
    if (last) {
        tmp = self->last;
        Py_INCREF(last);
        self->last = last;
        Py_XDECREF(tmp);
    }
    return 0;
}

通过填充 tp_init 槽。

.tp_init = (initproc) Custom_init,

tp_init 槽在 Python 中公开为 __init__() 方法。 它用于在创建对象后对其进行初始化。 初始化器总是接受位置和关键字参数,并且它们应该在成功时返回 0 或在错误时返回 -1

tp_new 处理程序不同,根本无法保证调用 tp_init(例如,pickle 模块默认不调用 __init__()在未腌制的情况下)。 也可以多次调用。 任何人都可以在我们的对象上调用 __init__() 方法。 出于这个原因,我们在分配新的属性值时必须格外小心。 我们可能会受到诱惑,例如像这样分配 first 成员:

if (first) {
    Py_XDECREF(self->first);
    Py_INCREF(first);
    self->first = first;
}

但这将是有风险的。 我们的类型不限制 first 成员的类型,所以它可以是任何类型的对象。 它可能有一个析构函数,导致尝试访问 first 成员的代码被执行; 或者那个析构函数可以释放 全局解释器锁 并让任意代码在访问和修改我们对象的其他线程中运行。

为了偏执并保护自己免受这种可能性的影响,我们几乎总是在减少成员的引用计数之前重新分配成员。 我们什么时候不需要这样做?

  • 当我们绝对知道引用计数大于 1 时;
  • 当我们知道对象 1 的释放既不会释放 GIL 也不会导致任何回调到我们类型的代码中时;
  • 在不支持循环垃圾回收 2 的类型的 tp_dealloc 处理程序中递减引用计数时。

我们想将我们的实例变量公开为属性。 有很多方法可以做到这一点。 最简单的方法是定义成员定义:

static PyMemberDef Custom_members[] = {
    {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
     "first name"},
    {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,
     "last name"},
    {"number", T_INT, offsetof(CustomObject, number), 0,
     "custom number"},
    {NULL}  /* Sentinel */
};

并将定义放在 tp_members 插槽中:

.tp_members = Custom_members,

每个成员定义都有一个成员名称、类型、偏移量、访问标志和文档字符串。 有关详细信息,请参阅下面的 通用属性管理 部分。

这种方法的一个缺点是它没有提供一种方法来限制可以分配给 Python 属性的对象类型。 我们希望名字和姓氏是字符串,但可以分配任何 Python 对象。 此外,可以删除属性,将 C 指针设置为 NULL。 即使我们可以确保成员被初始化为非 NULL 值,如果删除属性,成员可以设置为 NULL

我们定义了一个方法,Custom.name(),它将对象名称输出为名字和姓氏的串联。

static PyObject *
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
{
    if (self->first == NULL) {
        PyErr_SetString(PyExc_AttributeError, "first");
        return NULL;
    }
    if (self->last == NULL) {
        PyErr_SetString(PyExc_AttributeError, "last");
        return NULL;
    }
    return PyUnicode_FromFormat("%S %S", self->first, self->last);
}

该方法被实现为一个 C 函数,它采用 Custom(或 Custom 子类)实例作为第一个参数。 方法总是将一个实例作为第一个参数。 方法通常也接受位置参数和关键字参数,但在这种情况下,我们不接受任何参数,也不需要接受位置参数元组或关键字参数字典。 此方法等效于 Python 方法:

def name(self):
    return "%s %s" % (self.first, self.last)

请注意,我们必须检查我们的 firstlast 成员是 NULL 的可能性。 这是因为它们可以被删除,在这种情况下它们被设置为 NULL。 最好防止删除这些属性并将属性值限制为字符串。 我们将在下一节中看到如何做到这一点。

现在我们已经定义了方法,我们需要创建一个方法定义数组:

static PyMethodDef Custom_methods[] = {
    {"name", (PyCFunction) Custom_name, METH_NOARGS,
     "Return the name, combining the first and last name"
    },
    {NULL}  /* Sentinel */
};

(请注意,我们使用 METH_NOARGS 标志来指示该方法除了 self 之外没有其他参数)

并将其分配给 tp_methods 插槽:

.tp_methods = Custom_methods,

最后,我们将使我们的类型可用作子类化的基类。 到目前为止,我们已经仔细地编写了我们的方法,因此它们不会对正在创建或使用的对象的类型做出任何假设,所以我们需要做的就是将 Py_TPFLAGS_BASETYPE 添加到我们的类标志定义:

.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,

我们将 PyInit_custom() 重命名为 PyInit_custom2(),更新 PyModuleDef 结构中的模块名称,并更新 PyTypeObject 结构中的完整类名。

最后,我们更新 setup.py 文件以构建新模块:

from distutils.core import setup, Extension
setup(name="custom", version="1.0",
      ext_modules=[
         Extension("custom", ["custom.c"]),
         Extension("custom2", ["custom2.c"]),
         ])

2.3. 提供对数据属性的更精细控制

在本节中,我们将更好地控制如何在 Custom 示例中设置 firstlast 属性。 在我们模块的先前版本中,实例变量 firstlast 可以设置为非字符串值甚至删除。 我们要确保这些属性始终包含字符串。

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"

typedef struct {
    PyObject_HEAD
    PyObject *first; /* first name */
    PyObject *last;  /* last name */
    int number;
} CustomObject;

static void
Custom_dealloc(CustomObject *self)
{
    Py_XDECREF(self->first);
    Py_XDECREF(self->last);
    Py_TYPE(self)->tp_free((PyObject *) self);
}

static PyObject *
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    CustomObject *self;
    self = (CustomObject *) type->tp_alloc(type, 0);
    if (self != NULL) {
        self->first = PyUnicode_FromString("");
        if (self->first == NULL) {
            Py_DECREF(self);
            return NULL;
        }
        self->last = PyUnicode_FromString("");
        if (self->last == NULL) {
            Py_DECREF(self);
            return NULL;
        }
        self->number = 0;
    }
    return (PyObject *) self;
}

static int
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwlist[] = {"first", "last", "number", NULL};
    PyObject *first = NULL, *last = NULL, *tmp;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
                                     &first, &last,
                                     &self->number))
        return -1;

    if (first) {
        tmp = self->first;
        Py_INCREF(first);
        self->first = first;
        Py_DECREF(tmp);
    }
    if (last) {
        tmp = self->last;
        Py_INCREF(last);
        self->last = last;
        Py_DECREF(tmp);
    }
    return 0;
}

static PyMemberDef Custom_members[] = {
    {"number", T_INT, offsetof(CustomObject, number), 0,
     "custom number"},
    {NULL}  /* Sentinel */
};

static PyObject *
Custom_getfirst(CustomObject *self, void *closure)
{
    Py_INCREF(self->first);
    return self->first;
}

static int
Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
{
    PyObject *tmp;
    if (value == NULL) {
        PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
        return -1;
    }
    if (!PyUnicode_Check(value)) {
        PyErr_SetString(PyExc_TypeError,
                        "The first attribute value must be a string");
        return -1;
    }
    tmp = self->first;
    Py_INCREF(value);
    self->first = value;
    Py_DECREF(tmp);
    return 0;
}

static PyObject *
Custom_getlast(CustomObject *self, void *closure)
{
    Py_INCREF(self->last);
    return self->last;
}

static int
Custom_setlast(CustomObject *self, PyObject *value, void *closure)
{
    PyObject *tmp;
    if (value == NULL) {
        PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
        return -1;
    }
    if (!PyUnicode_Check(value)) {
        PyErr_SetString(PyExc_TypeError,
                        "The last attribute value must be a string");
        return -1;
    }
    tmp = self->last;
    Py_INCREF(value);
    self->last = value;
    Py_DECREF(tmp);
    return 0;
}

static PyGetSetDef Custom_getsetters[] = {
    {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
     "first name", NULL},
    {"last", (getter) Custom_getlast, (setter) Custom_setlast,
     "last name", NULL},
    {NULL}  /* Sentinel */
};

static PyObject *
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
{
    return PyUnicode_FromFormat("%S %S", self->first, self->last);
}

static PyMethodDef Custom_methods[] = {
    {"name", (PyCFunction) Custom_name, METH_NOARGS,
     "Return the name, combining the first and last name"
    },
    {NULL}  /* Sentinel */
};

static PyTypeObject CustomType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "custom3.Custom",
    .tp_doc = "Custom objects",
    .tp_basicsize = sizeof(CustomObject),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
    .tp_new = Custom_new,
    .tp_init = (initproc) Custom_init,
    .tp_dealloc = (destructor) Custom_dealloc,
    .tp_members = Custom_members,
    .tp_methods = Custom_methods,
    .tp_getset = Custom_getsetters,
};

static PyModuleDef custommodule = {
    PyModuleDef_HEAD_INIT,
    .m_name = "custom3",
    .m_doc = "Example module that creates an extension type.",
    .m_size = -1,
};

PyMODINIT_FUNC
PyInit_custom3(void)
{
    PyObject *m;
    if (PyType_Ready(&CustomType) < 0)
        return NULL;

    m = PyModule_Create(&custommodule);
    if (m == NULL)
        return NULL;

    Py_INCREF(&CustomType);
    if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
        Py_DECREF(&CustomType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

为了更好地控制 firstlast 属性,我们将使用自定义的 getter 和 setter 函数。 以下是获取和设置 first 属性的函数:

static PyObject *
Custom_getfirst(CustomObject *self, void *closure)
{
    Py_INCREF(self->first);
    return self->first;
}

static int
Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
{
    PyObject *tmp;
    if (value == NULL) {
        PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
        return -1;
    }
    if (!PyUnicode_Check(value)) {
        PyErr_SetString(PyExc_TypeError,
                        "The first attribute value must be a string");
        return -1;
    }
    tmp = self->first;
    Py_INCREF(value);
    self->first = value;
    Py_DECREF(tmp);
    return 0;
}

getter 函数被传递一个 Custom 对象和一个“闭包”,它是一个空指针。 在这种情况下,闭包将被忽略。 (闭包支持将定义数据传递给 getter 和 setter 的高级用法。 例如,这可以用于允许一组 getter 和 setter 函数根据闭包中的数据决定要获取或设置的属性。)

setter 函数传递 Custom 对象、新值和闭包。 新值可能是 NULL,在这种情况下,该属性将被删除。 在我们的 setter 中,如果属性被删除或者它的新值不是字符串,我们会引发错误。

我们创建一个 PyGetSetDef 结构数组:

static PyGetSetDef Custom_getsetters[] = {
    {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
     "first name", NULL},
    {"last", (getter) Custom_getlast, (setter) Custom_setlast,
     "last name", NULL},
    {NULL}  /* Sentinel */
};

并将其注册到 tp_getset 插槽中:

.tp_getset = Custom_getsetters,

PyGetSetDef 结构中的最后一项是上面提到的“闭包”。 在这种情况下,我们没有使用闭包,所以我们只传递 NULL

我们还删除了这些属性的成员定义:

static PyMemberDef Custom_members[] = {
    {"number", T_INT, offsetof(CustomObject, number), 0,
     "custom number"},
    {NULL}  /* Sentinel */
};

我们还需要更新 tp_init 处理程序以只允许传递字符串 3

static int
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwlist[] = {"first", "last", "number", NULL};
    PyObject *first = NULL, *last = NULL, *tmp;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
                                     &first, &last,
                                     &self->number))
        return -1;

    if (first) {
        tmp = self->first;
        Py_INCREF(first);
        self->first = first;
        Py_DECREF(tmp);
    }
    if (last) {
        tmp = self->last;
        Py_INCREF(last);
        self->last = last;
        Py_DECREF(tmp);
    }
    return 0;
}

通过这些更改,我们可以确保 firstlast 成员永远不会是 NULL,因此我们可以在几乎所有情况下删除对 NULL 值的检查。 这意味着大部分 Py_XDECREF() 调用可以转换为 Py_DECREF() 调用。 我们唯一不能改变这些调用的地方是在 tp_dealloc 实现中,这里有可能在 tp_new 中这些成员的初始化失败。

我们还在初始化函数中重命名了模块初始化函数和模块名称,就像我们之前所做的那样,我们在 setup.py 文件中添加了一个额外的定义。


2.4. 支持循环垃圾回收

Python 有一个 循环垃圾收集器 (GC),即使它们的引用计数不为零,它也可以识别不需要的对象。 当对象参与循环时,就会发生这种情况。 例如,考虑:

>>> l = []
>>> l.append(l)
>>> del l

在本例中,我们创建了一个包含自身的列表。 当我们删除它时,它仍然有一个来自它自己的引用。 它的引用计数不会降到零。 幸运的是,Python 的循环垃圾收集器最终会发现列表是垃圾并释放它。

Custom 示例的第二个版本中,我们允许将任何类型的对象存储在 firstlast 属性 4 中。 此外,在第二和第三个版本中,我们允许子类化Custom,子类可以添加任意属性。 由于这两个原因中的任何一个,Custom 对象可以参与循环:

>>> import custom3
>>> class Derived(custom3.Custom): pass
...
>>> n = Derived()
>>> n.some_attribute = n

为了允许循环 GC 正确检测和收集参与引用循环的 Custom 实例,我们的 Custom 类型需要填充两个额外的插槽并启用启用这些插槽的标志:

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"

typedef struct {
    PyObject_HEAD
    PyObject *first; /* first name */
    PyObject *last;  /* last name */
    int number;
} CustomObject;

static int
Custom_traverse(CustomObject *self, visitproc visit, void *arg)
{
    Py_VISIT(self->first);
    Py_VISIT(self->last);
    return 0;
}

static int
Custom_clear(CustomObject *self)
{
    Py_CLEAR(self->first);
    Py_CLEAR(self->last);
    return 0;
}

static void
Custom_dealloc(CustomObject *self)
{
    PyObject_GC_UnTrack(self);
    Custom_clear(self);
    Py_TYPE(self)->tp_free((PyObject *) self);
}

static PyObject *
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    CustomObject *self;
    self = (CustomObject *) type->tp_alloc(type, 0);
    if (self != NULL) {
        self->first = PyUnicode_FromString("");
        if (self->first == NULL) {
            Py_DECREF(self);
            return NULL;
        }
        self->last = PyUnicode_FromString("");
        if (self->last == NULL) {
            Py_DECREF(self);
            return NULL;
        }
        self->number = 0;
    }
    return (PyObject *) self;
}

static int
Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwlist[] = {"first", "last", "number", NULL};
    PyObject *first = NULL, *last = NULL, *tmp;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
                                     &first, &last,
                                     &self->number))
        return -1;

    if (first) {
        tmp = self->first;
        Py_INCREF(first);
        self->first = first;
        Py_DECREF(tmp);
    }
    if (last) {
        tmp = self->last;
        Py_INCREF(last);
        self->last = last;
        Py_DECREF(tmp);
    }
    return 0;
}

static PyMemberDef Custom_members[] = {
    {"number", T_INT, offsetof(CustomObject, number), 0,
     "custom number"},
    {NULL}  /* Sentinel */
};

static PyObject *
Custom_getfirst(CustomObject *self, void *closure)
{
    Py_INCREF(self->first);
    return self->first;
}

static int
Custom_setfirst(CustomObject *self, PyObject *value, void *closure)
{
    if (value == NULL) {
        PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
        return -1;
    }
    if (!PyUnicode_Check(value)) {
        PyErr_SetString(PyExc_TypeError,
                        "The first attribute value must be a string");
        return -1;
    }
    Py_INCREF(value);
    Py_CLEAR(self->first);
    self->first = value;
    return 0;
}

static PyObject *
Custom_getlast(CustomObject *self, void *closure)
{
    Py_INCREF(self->last);
    return self->last;
}

static int
Custom_setlast(CustomObject *self, PyObject *value, void *closure)
{
    if (value == NULL) {
        PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
        return -1;
    }
    if (!PyUnicode_Check(value)) {
        PyErr_SetString(PyExc_TypeError,
                        "The last attribute value must be a string");
        return -1;
    }
    Py_INCREF(value);
    Py_CLEAR(self->last);
    self->last = value;
    return 0;
}

static PyGetSetDef Custom_getsetters[] = {
    {"first", (getter) Custom_getfirst, (setter) Custom_setfirst,
     "first name", NULL},
    {"last", (getter) Custom_getlast, (setter) Custom_setlast,
     "last name", NULL},
    {NULL}  /* Sentinel */
};

static PyObject *
Custom_name(CustomObject *self, PyObject *Py_UNUSED(ignored))
{
    return PyUnicode_FromFormat("%S %S", self->first, self->last);
}

static PyMethodDef Custom_methods[] = {
    {"name", (PyCFunction) Custom_name, METH_NOARGS,
     "Return the name, combining the first and last name"
    },
    {NULL}  /* Sentinel */
};

static PyTypeObject CustomType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "custom4.Custom",
    .tp_doc = "Custom objects",
    .tp_basicsize = sizeof(CustomObject),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
    .tp_new = Custom_new,
    .tp_init = (initproc) Custom_init,
    .tp_dealloc = (destructor) Custom_dealloc,
    .tp_traverse = (traverseproc) Custom_traverse,
    .tp_clear = (inquiry) Custom_clear,
    .tp_members = Custom_members,
    .tp_methods = Custom_methods,
    .tp_getset = Custom_getsetters,
};

static PyModuleDef custommodule = {
    PyModuleDef_HEAD_INIT,
    .m_name = "custom4",
    .m_doc = "Example module that creates an extension type.",
    .m_size = -1,
};

PyMODINIT_FUNC
PyInit_custom4(void)
{
    PyObject *m;
    if (PyType_Ready(&CustomType) < 0)
        return NULL;

    m = PyModule_Create(&custommodule);
    if (m == NULL)
        return NULL;

    Py_INCREF(&CustomType);
    if (PyModule_AddObject(m, "Custom", (PyObject *) &CustomType) < 0) {
        Py_DECREF(&CustomType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

首先,遍历方法让循环 GC 知道可以参与循环的子对象:

static int
Custom_traverse(CustomObject *self, visitproc visit, void *arg)
{
    int vret;
    if (self->first) {
        vret = visit(self->first, arg);
        if (vret != 0)
            return vret;
    }
    if (self->last) {
        vret = visit(self->last, arg);
        if (vret != 0)
            return vret;
    }
    return 0;
}

对于每个可以参与循环的子对象,我们需要调用visit()函数,该函数传递给遍历方法。 visit() 函数将子对象和传递给遍历方法的额外参数 arg 作为参数。 它返回一个整数值,如果它不为零,则必须返回该值。

Python 提供了一个 Py_VISIT() 宏,可以自动调用访问函数。 使用 Py_VISIT(),我们可以最小化 Custom_traverse 中的样板数量:

static int
Custom_traverse(CustomObject *self, visitproc visit, void *arg)
{
    Py_VISIT(self->first);
    Py_VISIT(self->last);
    return 0;
}

笔记

tp_traverse 实现必须准确命名其参数 visitarg,以便使用 Py_VISIT()


其次,我们需要提供一种清除任何可以参与循环的子对象的方法:

static int
Custom_clear(CustomObject *self)
{
    Py_CLEAR(self->first);
    Py_CLEAR(self->last);
    return 0;
}

注意 Py_CLEAR() 宏的使用。 这是在减少引用计数的同时清除任意类型数据属性的推荐且安全的方法。 如果您在将属性设置为 NULL 之前调用 Py_XDECREF(),则该属性的析构函数可能会回调到再次读取该属性的代码([X212X ] 尤其是 如果有参考循环)。

笔记

您可以通过编写来模拟 Py_CLEAR()

PyObject *tmp;
tmp = self->first;
self->first = NULL;
Py_XDECREF(tmp);

尽管如此,在删除属性时始终使用 Py_CLEAR() 更容易且不易出错。 不要试图以牺牲健壮性为代价进行微观优化!


清除属性时,deallocator Custom_dealloc 可能会调用任意代码。 这意味着可以在函数内部触发循环 GC。 由于 GC 假定引用计数不为零,因此我们需要在清除成员之前通过调用 PyObject_GC_UnTrack() 来从 GC 中取消跟踪对象。 这是我们使用 PyObject_GC_UnTrack()Custom_clear 重新实现的释放器:

static void
Custom_dealloc(CustomObject *self)
{
    PyObject_GC_UnTrack(self);
    Custom_clear(self);
    Py_TYPE(self)->tp_free((PyObject *) self);
}

最后,我们将 Py_TPFLAGS_HAVE_GC 标志添加到类标志中:

.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,

差不多就是这样。 如果我们编写了自定义的 tp_alloctp_free 处理程序,我们需要修改它们以进行循环垃圾收集。 大多数扩展将使用自动提供的版本。


2.5. 子类化其他类型

可以创建从现有类型派生的新扩展类型。 从内置类型继承是最容易的,因为扩展可以轻松使用它需要的 PyTypeObject。 在扩展模块之间共享这些 PyTypeObject 结构可能很困难。

在这个例子中,我们将创建一个从内置 list 类型继承的 SubList 类型。 新类型将与常规列表完全兼容,但会有一个额外的 increment() 方法来增加内部计数器:

>>> import sublist
>>> s = sublist.SubList(range(3))
>>> s.extend(s)
>>> print(len(s))
6
>>> print(s.increment())
1
>>> print(s.increment())
2
#define PY_SSIZE_T_CLEAN
#include <Python.h>

typedef struct {
    PyListObject list;
    int state;
} SubListObject;

static PyObject *
SubList_increment(SubListObject *self, PyObject *unused)
{
    self->state++;
    return PyLong_FromLong(self->state);
}

static PyMethodDef SubList_methods[] = {
    {"increment", (PyCFunction) SubList_increment, METH_NOARGS,
     PyDoc_STR("increment state counter")},
    {NULL},
};

static int
SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
{
    if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
        return -1;
    self->state = 0;
    return 0;
}

static PyTypeObject SubListType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "sublist.SubList",
    .tp_doc = "SubList objects",
    .tp_basicsize = sizeof(SubListObject),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
    .tp_init = (initproc) SubList_init,
    .tp_methods = SubList_methods,
};

static PyModuleDef sublistmodule = {
    PyModuleDef_HEAD_INIT,
    .m_name = "sublist",
    .m_doc = "Example module that creates an extension type.",
    .m_size = -1,
};

PyMODINIT_FUNC
PyInit_sublist(void)
{
    PyObject *m;
    SubListType.tp_base = &PyList_Type;
    if (PyType_Ready(&SubListType) < 0)
        return NULL;

    m = PyModule_Create(&sublistmodule);
    if (m == NULL)
        return NULL;

    Py_INCREF(&SubListType);
    if (PyModule_AddObject(m, "SubList", (PyObject *) &SubListType) < 0) {
        Py_DECREF(&SubListType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

如您所见,源代码与前几节中的 Custom 示例非常相似。 我们将分解它们之间的主要区别。

typedef struct {
    PyListObject list;
    int state;
} SubListObject;

派生类型对象的主要区别在于基类型的对象结构必须是第一个值。 基本类型将在其结构的开头包含 PyObject_HEAD()

当 Python 对象是 SubList 实例时,其 PyObject * 指针可以安全地转换为 PyListObject *SubListObject *

static int
SubList_init(SubListObject *self, PyObject *args, PyObject *kwds)
{
    if (PyList_Type.tp_init((PyObject *) self, args, kwds) < 0)
        return -1;
    self->state = 0;
    return 0;
}

我们在上面看到如何调用基本类型的 __init__ 方法。

在编写具有自定义 tp_newtp_dealloc 成员的类型时,此模式很重要。 tp_new 处理程序实际上不应该用它的 tp_alloc 为对象创建内存,而是让基类通过调用它自己的 tp_new 来处理它。

PyTypeObject 结构支持 tp_base 指定类型的具体基类。 由于跨平台编译器问题,您不能直接使用对 PyList_Type 的引用来填充该字段; 它应该稍后在模块初始化函数中完成:

PyMODINIT_FUNC
PyInit_sublist(void)
{
    PyObject* m;
    SubListType.tp_base = &PyList_Type;
    if (PyType_Ready(&SubListType) < 0)
        return NULL;

    m = PyModule_Create(&sublistmodule);
    if (m == NULL)
        return NULL;

    Py_INCREF(&SubListType);
    if (PyModule_AddObject(m, "SubList", (PyObject *) &SubListType) < 0) {
        Py_DECREF(&SubListType);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

在调用 PyType_Ready() 之前,类型结构必须填充 tp_base 槽。 当我们派生一个现有类型时,没有必要用 PyType_GenericNew() 填充 tp_alloc 槽——将继承基类型的分配函数。

之后,调用 PyType_Ready() 并将类型对象添加到模块与基本的 Custom 示例相同。

脚注

1
当我们知道对象是一个基本类型时,这是真的,比如字符串或浮点数。
2
在本例中,我们在 tp_dealloc 处理程序中依赖于此,因为我们的类型不支持垃圾收集。
3
我们现在知道第一个和最后一个成员是字符串,所以也许我们可以在减少它们的引用计数时不那么小心,但是,我们接受字符串子类的实例。 即使释放普通字符串不会回调到我们的对象中,我们也不能保证释放字符串子类的实例不会回调到我们的对象中。
4
此外,即使我们的属性仅限于字符串实例,用户也可以传递任意 str 子类,因此仍然创建引用循环。