Search This Blog

Labels

Friday, December 17, 2010

《扩展和嵌入Python解释器(译稿)》2.1 The Basics

2.1 The Basics 基础知识

The Python runtime sees all Python objects as variables of type PyObject*. A PyObjectis not a very magnificent object – it just contains the refcount and a pointer to the object’s “type object”. This is where the action is; the type object determines which (C) functions get called when, for instance, an attribute gets looked up on an object or it is multiplied by another object. These C functions are called “type methods” to distinguish them from things like [].append(which we call “object methods”).

Python runtime将所有Python对象都看作PyObject*类型的变量。PyObject并非是一个宏大的对象——它仅含有引用计数和指向对象的“类型对象”的指针,这是对象表现行为的地方,例如当在一个对象上查找某一属性或与另一个对象相乘时,就是由类型对象确定是哪一个函数(C函数)被调用。这些函数称之为“类型方法”,区别于像[].append之类的方法(我们称之为“实例方法”)

So, if you want to define a new object 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 complete, module that defines a new type:

这类的事只能用例子来解释,所以这里有一个定义新类型的极小但却完整的模块:

#include <Python.h>

typedef struct {

 PyObject_HEAD

 /* Type-specific fields go here. */

} noddy_NoddyObject;

static PyTypeObject noddy_NoddyType = {

 PyObject_HEAD_INIT(NULL)

 0, /*ob_size*/

 "noddy.Noddy", /*tp_name*/

 sizeof(noddy_NoddyObject), /*tp_basicsize*/

 0, /*tp_itemsize*/

 0, /*tp_dealloc*/

 0, /*tp_print*/

 0, /*tp_getattr*/

 0, /*tp_setattr*/

 0, /*tp_compare*/

 0, /*tp_repr*/

 0, /*tp_as_number*/

 0, /*tp_as_sequence*/

 0, /*tp_as_mapping*/

 0, /*tp_hash */

 0, /*tp_call*/

 0, /*tp_str*/

 0, /*tp_getattro*/

 0, /*tp_setattro*/

 0, /*tp_as_buffer*/

 Py_TPFLAGS_DEFAULT, /*tp_flags*/

 "Noddy objects", /* tp_doc */

};

static PyMethodDef noddy_methods[] = {

 {NULL} /* Sentinel */

};

#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */

#define PyMODINIT_FUNC void

#endif

PyMODINIT_FUNC

initnoddy(void)

{

 PyObject* m;

 noddy_NoddyType.tp_new = PyType_GenericNew;

 if (PyType_Ready(&noddy_NoddyType) < 0)

  return;

 m = Py_InitModule3("noddy", noddy_methods, "Example module that creates an extension type.");

 Py_INCREF(&noddy_NoddyType);

 PyModule_AddObject(m, "Noddy", (PyObject *)&noddy_NoddyType);

}

Now that’s quite a bit to take in at once, but hopefully bits will seem familiar from the last chapter.

The first bit that will be new is:

现在有很多内容要一下子理解,但愿很多和上一章看起来相似。

第一处新的内容是:

typedef struct {

 PyObject_HEAD

} noddy_NoddyObject;

This is what a Noddy object will contain—in this case, nothing more than every Python object contains, namely a refcount and a pointer to a type object. These are the fields thePyObject_HEADmacro brings in. The reason for the macro is to standardize the layout and to enable special debugging fields in debug builds. Note that there is no semicolon after thePyObject_HEADmacro; one is included in the macro definition. Be wary of adding one by accident; it’s easy to do from habit, and your compiler might not complain, but someone else’s probably will! (On Windows, MSVC is known to call this an error and refuse to compile the code.)

这就是一个Noddy对象将包含的内容——在这个例子中,只是包含了每个对象都会包含的内容,即一个引用计数和一个指向类型对象的指针。这些是由宏PyObject_HEAD带入的;这个宏存在的理由在于标准化布局和在debug模式构建时可以指定调试域(debugging fields)。注意在宏后面没有分号;分号已经包含进了宏定义。小心不要偶然加了一个进来,这很容易由习惯导致,并且你的编译器可能不会报错,但其他人的可能就会!(在Windows 下,已知MSVC 会把这当作错误并会拒绝编译代码。)

For contrast, let’s take a look at the corresponding definition for standard Python integers:

作为对照,让我们看一下标准Python整数类型对应的定义:

typedef struct {

 PyObject_HEAD

 long ob_ival;

} PyIntObject;

Moving on, we come to the crunch— the type object.

继续,我们来啃硬骨头——类型对象。

static PyTypeObject noddy_NoddyType = {

 PyObject_HEAD_INIT(NULL)

 0,                               /*ob_size*/

 "noddy.Noddy", /*tp_name*/

 sizeof(noddy_NoddyObject),  /*tp_basicsize*/

 0,                               /*tp_itemsize*/

 0,                               /*tp_dealloc*/

 0,                               /*tp_print*/

 0,                               /*tp_getattr*/

 0,                               /*tp_setattr*/

 0,                               /*tp_compare*/

 0,                               /*tp_repr*/

 0,                               /*tp_as_number*/

 0,                               /*tp_as_sequence*/

 0,                               /*tp_as_mapping*/

 0,                               /*tp_hash */

 0,                               /*tp_call*/

 0,                               /*tp_str*/

 0,                               /*tp_getattro*/

 0,                               /*tp_setattro*/

 0,                               /*tp_as_buffer*/

 Py_TPFLAGS_DEFAULT,           /*tp_flags*/

 "Noddy objects",               /* tp_doc */

};

Now if you go and look up the definition of PyTypeObjectin ‘object.h’ you’ll see that it has many more fields that 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.

现在如果你去看看object.h 中PyTypeObject 的定义你会发现它比上面的定义多很多域。余下的域会被C编译器填充为0,通常的惯例是不要显式地指定它们,除非你确实需要。

This is so important that we’re going to pick the top of it apart still further:

这个是如此重要以致我们要将它的头部进一步分解出来。

PyObject_HEAD_INIT(NULL)

This line is a bit of a wart; what we’d like to write is:

这一行有些瑕疵;我们更愿写的是:

PyObject_HEAD_INIT(&PyType_Type)

as the type of a type object is “type”, but this isn’t strictly conforming C and some compilers complain.Fortunately, this member will be filled in for us by PyType_Ready().

尽管类型对象的类型是“type”,但这并没有严格遵循 C 标准而且有些编译器会报错。幸运的是,这个成员会由PyType_Ready() 来为我们填充。

0,                                    /* ob_size */

The ob_size field of the header is not used; its presence in the type structure is a historical artifactthat is maintained for binary compatibility with extension modules compiled for older versions of Python. Always set this field to zero.

头部的ob_size域没有使用,它在type 结构体中的出现是一个历史遗留,用于保持与由老版本 Python 编译的扩展模块的兼容性。总将这个域设为0。

"noddy.Noddy", /* tp_name */

The name of our type. This will appear in the default textual representation of our objects and in some error messages, for example:

我们的类型的命名,会出现在我们的对象的默认文本表示和一些错误信息中,例如:

>>> "" + noddy.new_noddy()

Traceback (most recent call last):

File "<stdin>", line 1, in ?

TypeError: cannot add type "noddy.Noddy" to string

Note that the name is a dotted name that includes both the module name and the name of the type within the module. The module in this case is noddy and the type is Noddy, so we set the type name to noddy.Noddy.

注意这个命名是一个包括模块名和模块中类型名的带句点命名。在这,模块的名字是noddy,类型的名字是Noddy,故我们设置类型名为 noddy.Noddy。

sizeof(noddy_NoddyObject),  /* tp_basicsize */

This is so that Python knows how much memory to allocate when you call PyObject_New().

这就是当你调用PyObject_New() 时Python所知道的分配内存的数量

Note:If you want your type to be subclassable from Python, and your type has the sametp_basicsize 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 __bases__, or else it will not be able to call your type’s __new__method without getting an error. You can avoid this problem by ensuring that your type has a larger value for tp_basicsizethan its base type does. Most of the time, this will be true anyway, because either your base type will be object, or else you will be adding data members to your base type, and therefore increasing its size.

0,                         /* tp_itemsize */

注意:如果你想要你的类型在Python中是可子类化的,并且你的类型和其基类有同样的tp_basicsize,对于多重继承你可能会有些问题。你的类型的一个Python 子类将必须将你的类型列入它的__bases__,否则它将不能无错地调用你的类型的 __new__ 方法。你可以通过使你的类型有比基类中更大的tp_basics ize值。大多数时候,总是这样的,因为要么你的基类是object 类,要么你会为你的基类添加数据成员,这样就增加了它的大小。

This has to do with variable length objects like lists and strings. Ignore this for now.

这与像列表和字符串这样的变长对象有关。目前可以忽略。

Skipping a number of type methods that we don’t provide, we set the class flags toPy_TPFLAGS_DEFAULT.

Py_TPFLAGS_DEFAULT, /*tp_flags*/

我们设置类标识(class flags)为Py_TPFLAGS_DEFAULT,来略过许多我们没有提供的类型方法,

All types should include this constant in their flags. It enables all of the members defined by the current version of Python.

所有的类型都应当在其标识域(flags)中包含这个常量。它能使所有的成员都是由Python 的当前版本定义的。

We provide a doc string for the type in tp_doc.

"Noddy objects", /* tp_doc */

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

Now we get into the type methods, the things that make your objects different from the others. We aren’t going to implement any of these in this version of the module. We’ll expand this example later to have more interesting behavior.

现在我们来着手类型方法,这是使你的对象异于其他对象的地方。我们不打算在这个模块中实现任何一个类型方法。在后面我们将扩充这个例子使之有更令人感兴趣的行为。

For now, all we want to be able to do is to create new Noddy objects. To enable object creation, we have to provide a tp_newimplementation. In this case, we can just use the default implementation provided by the API function PyType_GenericNew(). We’d like to just assign this to the tp_newslot, but we can’t, for portability sake, On some platforms or compilers, we can’t statically initialize a structure member with a function defined in another C module, so,instead, we’ll assign the tp_newslot in the module initialization function just before callingPyType_Ready():

目前,我们所想做的就是创建新的Noddy对象。为使对象能创建,我们必须提供tp_new的一个实现。在这个情况中,我们就使用由API函数PyType_GenericNew() 提供的默认的实现。我们打算赋值给 tp_new 域,但我们不能这样,因为可移植性的缘故,在有些平台或编译器上,我们不能用定义在另一个C 模块中的函数对结构体成员进行静态初始化。所以,作为代替,我们在调用PyType_Ready()之前,在模块初始化函数中对 tp_new 域赋值:

noddy_NoddyType.tp_new = PyType_GenericNew;

if (PyType_Ready(&noddy_NoddyType) < 0)

 return;

All the other type methods are NULL, so we’ll go over them later— that’s for a later section!

Everything else in the file should be familiar, except for some code in initnoddy():

所有其他的类型方法都为NULL,我们后面会来回顾——这是为下一节准备的!

文件中所有其他的都应是熟悉的,除了 initnoddy() 中的一些代码:

if (PyType_Ready(&noddy_NoddyType) < 0)

 return;

This initializes the Noddy type, filing in a number of members, including ob_typethat we initially set to NULL.

这初始化了Noddy 类型,填入了一些成员,包括我们初始时设为NULL的ob_type。

PyModule_AddObject(m, "Noddy", (PyObject *)&noddy_NoddyType);

This adds the type to the module dictionary. This allows us to create Noddy instances by calling theNoddy class:

这将类型加入了模块字典中。这允许我们通过调用Noddy 类来创建Noddy 实例。

>>> import noddy

>>> mynoddy = noddy.Noddy()

That’s it! All that remains is to build it; put the above code in a file called ‘noddy.c’ and

就是它!余下所有要做的就是构建它了;将上面的代码放在一个名为‘noddy.c’ 的文件中,并将

from distutils.core import setup, Extension

setup(name="noddy", version="1.0",

ext_modules=[Extension("noddy", ["noddy.c"])])

in a file called ‘setup.py’; then typing

放在一个名为‘setup.py’的文件中;然后输入

$ python setup.py build

at a shell should produce a file ‘noddy.so’ in a subdirectory; move to that directory and fire up Python — you should be able to import noddyand play around with Noddy objects.

到一个shell 窗口,会在子目录创建一个文件;进入那个目录启动——你就能够导入noddy(import noddy)和折腾 Noddy 对象了。

That wasn’t so hard, was it?

不是那么难的,是吧?

Of course, the current Noddy type is pretty uninteresting. It has no data and doesn’t do anything. It can’t even be subclassed.

当然,目前的 Noddy 类型比较没意思。它没有数据也不能做任何事。它甚至还不能子类化。

2.1.1 Adding data and methods to the Basic example 给基础示例添加数据和方法

Let’s expend the basic example to add some data and methods. Let’s also make the type usable as a base class. We’ll create a new module, noddy2 that adds these capabilities:

让我们来扩展该基础示例添加一些数据和方法。让我们同时也使该类型可作为基类使用。我们将创建一个新的模块,noddy2,添加了这些功能。

#include <Python.h>

#include "structmember.h"

typedef struct {

 PyObject_HEAD

 PyObject *first; /* first name */

 PyObject *last; /* last name */

 int number;

} Noddy;

static void

Noddy_dealloc(Noddy* self)

{

 Py_XDECREF(self->first);

 Py_XDECREF(self->last);

 self->ob_type->tp_free((PyObject*)self);

}

static PyObject *

Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)

{

 Noddy *self;

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

 if (self != NULL) {

  self->first = PyString_FromString("");

  if (self->first == NULL)

  {

   Py_DECREF(self);

   return NULL;

  }

  self->last = PyString_FromString("");

  if (self->last == NULL)

  {

   Py_DECREF(self);

   return NULL;

  }

  self->number = 0;

 }

 return (PyObject *)self;

}

static int

Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)

{

 PyObject *first=NULL, *last=NULL, *tmp;

 static char *kwlist[] = {"first", "last", "number", NULL};

 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 Noddy_members[] = {

 {"first", T_OBJECT_EX, offsetof(Noddy, first), 0,"first name"},

 {"last", T_OBJECT_EX, offsetof(Noddy, last), 0,"last name"},

 {"number", T_INT, offsetof(Noddy, number), 0,"noddy number"},

 {NULL} /* Sentinel */

};

static PyObject *

Noddy_name(Noddy* self)

{

 static PyObject *format = NULL;

 PyObject *args, *result;

 if (format == NULL) {

  format = PyString_FromString("%s %s");

  if (format == NULL)

   return NULL;

 }

 if (self->first == NULL) {

  PyErr_SetString(PyExc_AttributeError, "first");

  return NULL;

 }

 if (self->last == NULL) {

  PyErr_SetString(PyExc_AttributeError, "last");

  return NULL;

 }

 args = Py_BuildValue("OO", self->first, self->last);

 if (args == NULL)

  return NULL;

 result = PyString_Format(format, args);

 Py_DECREF(args);

 return result;

}

static PyMethodDef Noddy_methods[] = {

 {"name", (PyCFunction)Noddy_name, METH_NOARGS,"Return the name, combining the first and last name"},

 {NULL} /* Sentinel */

};

static PyTypeObject NoddyType = {

 PyObject_HEAD_INIT(NULL)

 0, /*ob_size*/

 "noddy.Noddy", /*tp_name*/

 sizeof(Noddy), /*tp_basicsize*/

 0, /*tp_itemsize*/

 (destructor)Noddy_dealloc, /*tp_dealloc*/

 0, /*tp_print*/

 0, /*tp_getattr*/

 0, /*tp_setattr*/

 0, /*tp_compare*/

 0, /*tp_repr*/

 0, /*tp_as_number*/

 0, /*tp_as_sequence*/

 0, /*tp_as_mapping*/

 0, /*tp_hash */

 0, /*tp_call*/

 0, /*tp_str*/

 0, /*tp_getattro*/

 0, /*tp_setattro*/

 0, /*tp_as_buffer*/

 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/

 "Noddy objects", /* tp_doc */

 0, /* tp_traverse */

 0, /* tp_clear */

 0, /* tp_richcompare */

 0, /* tp_weaklistoffset */

 0, /* tp_iter */

 0, /* tp_iternext */

 Noddy_methods, /* tp_methods */

 Noddy_members, /* tp_members */

 0, /* tp_getset */

 0, /* tp_base */

 0, /* tp_dict */

 0, /* tp_descr_get */

 0, /* tp_descr_set */

 0, /* tp_dictoffset */

 (initproc)Noddy_init, /* tp_init */

 0, /* tp_alloc */

 Noddy_new, /* tp_new */

};

static PyMethodDef module_methods[] = {

 {NULL}   /* Sentinel */

};

#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */

#define PyMODINIT_FUNC void

#endif

PyMODINIT_FUNC

initnoddy2(void)

{

 PyObject* m;

 if (PyType_Ready(&NoddyType) < 0)

  return;

 m = Py_InitModule3("noddy2", module_methods,"Example module that creates an extension type.");

 if (m == NULL)

  return;

 Py_INCREF(&NoddyType);

 PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);

}

This version of the module has a number of changes.

这个版本的模块有许多改变

We’ve added an extra include:

我们添加了一条include语句

#include "structmember.h"

This include provides declarations that we use to handle attributes, as described a bit later.

这条include语句提供了我们用于处理属性的声明,稍后有叙述。

The name of the Noddyobject structure has been shortened to Noddy. The type object name has been shortened to NoddyType.

Noddy对象结构体的命名已简写为Noddy,类型对象的命名已简写为NoddyType。

The Noddytype now has three data attributes, first, last, and number. The firstand lastvariables are Python strings containing first and last names. The number attribute is an integer.

Noddy类型现在有三个数据属性,firstlastnumberfirstlast是Python字符串变量存储的名和姓。number属性是一个整型数。

The object structure is updated accordingly:

对象结构体也相应地更新:

typedef struct {

 PyObject_HEAD

 PyObject *first;

 PyObject *last;

 int number;

} Noddy;

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:

因为我们现在有了数据要处理,我们要更加小心对象的分配和释放。至少,我们需要一个释放函数:

static void

Noddy_dealloc(Noddy* self)

{

 Py_XDECREF(self->first);

 Py_XDECREF(self->last);

 self->ob_type->tp_free((PyObject*)self);

}

which is assigned to the tp_deallocmember:

将其赋值给成员tp_dealloc

(destructor)Noddy_dealloc, /*tp_dealloc*/

This method decrements the reference counts of the two Python attributes. We usePy_XDECREF()here because the firstand lastmembers could be NULL. It then calls thetp_freemember of the object’s type to free the object’s memory. Note that the object’s type might not be NoddyType, because the object may be an instance of a subclass.

该方法减小两个Python属性的引用计数。我们在此用Py_XDECREF()是因为first和last成员可能为NULL。然后调用对象类型中的tp_free成员来释放对象的内存。注意对象的类型可能不是NoddyType,因为对象可能是子类的一个实例。

We want to make sure that the first and last names are initialized to empty strings, so we provide a new method:

我们想确保名和姓被初始化为空字符串,因此我们提供一个new方法:

static PyObject *

Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)

{

 Noddy *self;

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

 if (self != NULL) {

  self->first = PyString_FromString("");

  if (self->first == NULL)

  {

   Py_DECREF(self);

   return NULL;

  }

  self->last = PyString_FromString("");

  if (self->last == NULL)

  {

   Py_DECREF(self);

   return NULL;

  }

  self->number = 0;

 }

 return (PyObject *)self;

}

and install it in the tp_newmember:

并将其载入tp_new成员:

Noddy_new, /* tp_new */

The new member is responsible for creating (as opposed to initializing) objects of the type. It is exposed in Python as the __new__()method. See the paper titled “Unifying types and classes in Python” for a detailed discussion of the __new__()method. One reason to implement a new method is to assure the initial values of instance variables. In this case, we use the new method to make sure that the initial values of the members firstand lastare not NULL. If we didn’t care whether the initial values were NULL, we could have used PyType_-GenericNew()as our new method, as we did before. PyType_GenericNew()initializes all of the instance variable members to NULL.

new成员负责创建(相对于初始化)该类型的对象。在Python中显示为__new__()方法。关于__new__()方法详细的讨论可参考名为“Unifying types and classes in Python”的文章。实现new方法的原因之一是确保实例变量的初始值。在这个例子中,我们用new方法确保了成员first和last的初始值不为NULL。如果不在意初始值是否为NULL,我们可以使用PyType_-GenericNew()作为我们的new方法,就像我们前面做的那样。PyType_ GenericNew()将所有的实例变量成员初始化为NULL。

The new method is a static method that is passed the type being instantiated and any arguments passed when the type was called, and that returns the new object created. New methods always accept positional and keyword arguments, but they often ignore the arguments, leaving the argument handling to initializer methods. Note that if the type supports subclassing, the type passed may not be the type being defined. The new method calls the    tp_alloc slot to allocate memory. We don’t fill the tp_allocslot ourselves. Rather PyType_Ready()fills it for us by inheriting it from our base class, which is objectby default. Most types use the default allocation.

new方法是一个静态方法,传递已初始化的类型,当类型被调用时可传递任何参数。并返回新创建的对象。新方法总是接收位置参数和关键字参数,但经常忽略这些参数,把这参数留给初始化方法处理。注意,若类型支持子类化,传递的类型可能就不是定义的类型。方法调用域来分配内存。我们不用自已来填写tp_alloc域。而由我们从我们的基类继承来的方法为我们填写,object缺省就是这样的,绝大多数类型使用这种缺省的分配方式。。

Note:If you are creating a co-operative tp_new(one that calls a base type’s tp_newor__new__), 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 tp_newdirectly, or via type->tp_base->tp_new. 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 TypeError.)

注意:如果你在创建一个协同的tp_new域(调用基类的tp_new或__new__的方法),你绝对不要试图用方法递归顺序(method resolution order)在运行时来确定调用哪一个方法。每次都静态地确定你要调用的类型,并且直接调用它的tp_new,或经由type->tp_base->tp_new。如果你不这样做的话,从其他Python定义的类型继承而来并且是你类型的Python子类,可能就无法正确地工作。(特别是,你不可能创建一个这样的子类的实例,而不产生TypeError错误)

We provide an initialization function:

我们提供了一个初始化函数:

static int

Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)

{

 PyObject *first=NULL, *last=NULL, *tmp;

 static char *kwlist[] = {"first", "last", "number", NULL};

 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;

}

by filling the tp_init slot.

通过填充tp_init域:

(initproc)Noddy_init,       /* tp_init */

The tp_initslot is exposed in Python as the __init__()method. It is used to initialize an object after it’s created. Unlike the new method, we can’t guarantee that the initializer is called. The initializer isn’t called when unpickling objects and it can be overridden. Our initializer accepts arguments to provide initial values for our instance. Initializers always accept positional and keyword arguments.

tp_init域在Python中显示为__init__()方法。用于在对象创建后初始化对象。不像new方法,我们不能保证初始化器被调用。。当取存储一个对象时初始化器不能被调用,但它可以被重写。我们的初始化器接收参数为我们的实例提供初始值。初始化器总是接收位置参数和关键字参数。

Initializers can be called multiple times. Anyone can call the __init__()method on our objects. For this reason, we have to be extra careful when assigning the new values. We might be tempted, for example to assign the first member like this:

初始化器可被重复调用。任何人都可以调用我们对象的__init__()方法。因此,我们在赋新值时必须格外小心。我们可能会企图,例如像这样给成员赋值:

if (first) {

 Py_XDECREF(self->first);

 Py_INCREF(first);

 self->first = first;

}

But this would be risky. Our type doesn’t restrict the type of the firstmember, so it could be any kind of object. It could have a destructor that causes code to be executed that tries to access the firstmember. 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?

但这样是危险的。我们的类型不限制first成员的类型,因而它可以是任意对象。它可能有一个析构器会导致试图访问first成员的代码被执行。为了谨慎和防止自已出现这种可能性。我们应该总在减少它们的引用计数前重新指定新的成员。什么时候不必这样呢?

l when we absolutely know that the reference count is greater than 1

l when we know that deallocation of the object1 will not cause any calls back into our type’s code

l when decrementing a reference count in a tp_deallochandler when garbage-collections is not supported2

l 我们明确地知道引用计数大于1时

l 我们知道释放对象时不会引发任何回调进入我们类型的代码时

l 当不支持垃圾回收时,在tp_dealloc中减少引用计数时

We want to 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:

我们想以属性的方式来暴露我们的实例变量。有一些方法可以做到。最简单的方式就是定义成员:

static PyMemberDef Noddy_members[] = {

 {"first", T_OBJECT_EX, offsetof(Noddy, first), 0,"first name"},

 {"last", T_OBJECT_EX, offsetof(Noddy, last), 0,"last name"},

 {"number", T_INT, offsetof(Noddy, number), 0,"noddy number"},

 {NULL} /* Sentinel */

};

and put the definitions in the tp_membersslot:

然后把定义加入到tp_members域:

Noddy_members, /* tp_members */

Each member definition has a member name, type, offset, access flags and documentation string. See the “Generic Attribute Management” section below for details.

每个成员定义中包含成员名、类型、偏移量、访问标识和文档字符串。详情参见后面的“Generic Attribute Management”一节。

A disadvantage of this approach is that it doesn’t provide a way to restrict the 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 toNULL. Even though we can make sure the members are initialized to non-NULL values, the members can be set to NULL if the attributes are deleted.

这种方法的一个缺点是没有提供一个限制可赋值给Python属性的对象类型的方法。我们希望first和last为字符串,但可赋予任意Python对象。另外,属性可被删除,设置为CNULL指针。即使我们确保成员被初始化为非空值(non-NULL),成员还是可能被设为NULL如果属性被删除了的话。

——————————————————————————————————————————

1This is true when we know that the object is a basic type, like a string or a float.

1在我们知道对象是像这样的基础类型时,这是成立的。

2We relied on this in the tp_deallochandler in this example, because our type doesn’t support garbage collection. Even if a type       supports garbage collection, there are calls that can be made to “untrack” the object from garbage collection, however, these calls are advanced and not covered here.

2在这个例子中我我们依赖于tp_dealloc句柄,因为我们的类型不支持垃圾回收。即使一个类型支持垃圾回收,也有一些调用可以取消对象对垃圾回收器的关联,但这些调用都是高级话题,在这不讨论。

 

We define a single method, name, that outputs the objects name as the concatenation of the first and last names.

我们定义了一个单独的方法,输出first和last的连接作为对象的名字。

static PyObject *

Noddy_name(Noddy* self)

{

 static PyObject *format = NULL;

 PyObject *args, *result;

 if (format == NULL) {

  format = PyString_FromString("%s %s");

  if (format == NULL)

   return NULL;

 }

 if (self->first == NULL) {

 PyErr_SetString(PyExc_AttributeError, "first");

 return NULL;

 }

 if (self->last == NULL) {

 PyErr_SetString(PyExc_AttributeError, "last");

 return NULL;

 }

 args = Py_BuildValue("OO", self->first, self->last);

 if (args == NULL)

  return NULL;

 result = PyString_Format(format, args);

 Py_DECREF(args);

 return result;

}

The method is implemented as a C function that takes a Noddy(or Noddysubclass) 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 cased 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:

该方法以C函数的方式实现,以Noddy(或Noddy的子类)的实例作为第一个参数。方法总是接收一个实例作为第一个参数。方法也经常接收位置参数和关键字参数,但在这个例子中,我们不接收任何参数,也不需要接收位置参数元组或关键字参数字典。这个方法相当于Python方法。

def name(self):

return "%s %s" % (self.first, self.last)

Note that we have to check for the possibility that our firstand lastmembers are NULL. This is because they can be deleted, in which case they are set to NULL. 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.

注意,我们必须检查我们的成员first和last是否可能为NULL。这是因为它们可能会被删除,那个情况下它们会被设为NULL。更好的方法是阻止删除这些属性和限制属性的值为字符串。我们将在下一节看到怎样实现。

Now that we’ve defined the method, we need to create an array of method definitions:

既然我们已经定义方法,我们就要创建方法定义数组。

static PyMethodDef Noddy_methods[] = {

 {"name", (PyCFunction)Noddy_name, METH_NOARGS,"Return the name, combining the first and last name"},

 {NULL} /* Sentinel */

};

and assign them to the tp_methodsslot:

并将它们赋值给tp_methods域:

Noddy_methods,           /* tp_methods */

Note that we used the METH_NOARGSflag to indicate that the method is passed no arguments.

注意,我们用标识来指示方法没有传入任何参数

Finally, we’ll make our type usable as a base class. We’ve 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 Py_TPFLAGS_BASETYPEto our class flag definition:

最后,我们会使我们的类型可作为基类使用。到目前为止,我们已经仔细地写了我们的方法,它们不需要假设被创建或使用的对象的类型,所以我们所要做的全部就是给我们的类标志增加

Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/

We rename initnoddy()to initnoddy2()and update the module name passed toPy_InitModule3(). Finally, we update our ‘setup.py’ file to build the new module:

我们重命名initnoddy()为initnoddy2()并更新传入Py_InitModule3()的模块名。最后,我们更新‘setup.py’文件来构建新模块:

from distutils.core import setup, Extension

setup(name="noddy", version="1.0",

ext_modules=[

Extension("noddy", ["noddy.c"]),

Extension("noddy2", ["noddy2.c"]),

])

2.1.2 Providing finer control over data attributes 为数据属性提供更好的控制

In this section, we’ll provide finer control over how the firstandlastattributes are set in theNoddyexample. In the previous version of our module, the instance variables firstandlastcould be set to non-string values or even deleted. We want to make sure that these attributes always contain strings.

在这一部分,我们将为first和last属性在Noddy示例中的设置提供更好的控制。在我们模块的前一个版本中,实例变量first和last可设置为非字符串值或者甚至被删除。我们想确保这些属性总为字符串。

#include <Python.h>

#include "structmember.h"

typedef struct {

 PyObject_HEAD

 PyObject *first;

 PyObject *last;

 int number;

} Noddy;

static void

Noddy_dealloc(Noddy* self)

{

 Py_XDECREF(self->first);

 Py_XDECREF(self->last);

 self->ob_type->tp_free((PyObject*)self);

}

static PyObject *

Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)

{

 Noddy *self;

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

 if (self != NULL) {

  self->first = PyString_FromString("");

  if (self->first == NULL) 

  {

  Py_DECREF(self);

  return NULL;

  }

  self->last = PyString_FromString("");

  if (self->last == NULL)

  {

  Py_DECREF(self);

  return NULL;

  }

 self->number = 0;

 }

 return (PyObject *)self;

}

static int

Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)

{

 PyObject *first=NULL, *last=NULL, *tmp;

 static char *kwlist[] = {"first", "last", "number", NULL};

 if (! PyArg_ParseTupleAndKeywords(args, kwds, "|SSi", 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 Noddy_members[] = {

 {"number", T_INT, offsetof(Noddy, number), 0,"noddy number"},

 {NULL} /* Sentinel */

};

static PyObject *

Noddy_getfirst(Noddy *self, void *closure)

{

 Py_INCREF(self->first);

 return self->first;

}

static int

Noddy_setfirst(Noddy *self, PyObject *value, void *closure)

{

 if (value == NULL) {

  PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");

  return -1;

 }

 if (! PyString_Check(value)) {

  PyErr_SetString(PyExc_TypeError,"The first attribute value must be a string");

 return -1;

 }

 Py_DECREF(self->first);

 Py_INCREF(value);

 self->first = value;

 return 0;

}

static PyObject *

Noddy_getlast(Noddy *self, void *closure)

{

 Py_INCREF(self->last);

 return self->last;

}

static int

Noddy_setlast(Noddy *self, PyObject *value, void *closure)

{

 if (value == NULL) {

  PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");

 return -1;

 }

 if (! PyString_Check(value)) {

  PyErr_SetString(PyExc_TypeError,"The last attribute value must be a string");

 return -1;

 }

 Py_DECREF(self->last);

 Py_INCREF(value);

 self->last = value;

 return 0;

}

static PyGetSetDef Noddy_getseters[] = {

 {"first",(getter)Noddy_getfirst, (setter)Noddy_setfirst,"first name",NULL},

 {"last",(getter)Noddy_getlast, (setter)Noddy_setlast,"last name",NULL},

 {NULL} /* Sentinel */

};

static PyObject *

Noddy_name(Noddy* self)

{

 static PyObject *format = NULL;

 PyObject *args, *result;

 if (format == NULL) {

  format = PyString_FromString("%s %s");

  if (format == NULL)

   return NULL;

 }

 args = Py_BuildValue("OO", self->first, self->last);

 if (args == NULL)

  return NULL;

 result = PyString_Format(format, args);

 Py_DECREF(args);

 return result;

}

static PyMethodDef Noddy_methods[] = {

 {"name", (PyCFunction)Noddy_name, METH_NOARGS,"Return the name, combining the first and last name"},

 {NULL} /* Sentinel */

};

static PyTypeObject NoddyType = {

 PyObject_HEAD_INIT(NULL)

 0, /*ob_size*/

 "noddy.Noddy", /*tp_name*/

 sizeof(Noddy), /*tp_basicsize*/

 0, /*tp_itemsize*/

 (destructor)Noddy_dealloc, /*tp_dealloc*/

 0, /*tp_print*/

 0, /*tp_getattr*/

 0, /*tp_setattr*/

 0, /*tp_compare*/

 0, /*tp_repr*/

 0, /*tp_as_number*/

 0, /*tp_as_sequence*/

 0, /*tp_as_mapping*/

 0, /*tp_hash */

 0, /*tp_call*/

 0, /*tp_str*/

 0, /*tp_getattro*/

 0, /*tp_setattro*/

 0, /*tp_as_buffer*/

 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/

 "Noddy objects", /* tp_doc */

 0, /* tp_traverse */

 0, /* tp_clear */

 0, /* tp_richcompare */

 0, /* tp_weaklistoffset */

 0, /* tp_iter */

 0, /* tp_iternext */

 Noddy_methods, /* tp_methods */

 Noddy_members, /* tp_members */

 Noddy_getseters, /* tp_getset */

 0, /* tp_base */

 0, /* tp_dict */

 0, /* tp_descr_get */

 0, /* tp_descr_set */

 0, /* tp_dictoffset */

 (initproc)Noddy_init, /* tp_init */

 0, /* tp_alloc */

 Noddy_new, /* tp_new */

};

static PyMethodDef module_methods[] = {

 {NULL} /* Sentinel */

};

#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */

#define PyMODINIT_FUNC void

#endif

PyMODINIT_FUNC

initnoddy3(void)

{

 PyObject* m;

 if (PyType_Ready(&NoddyType) < 0)

  return;

 m = Py_InitModule3("noddy3", module_methods,"Example module that creates an extension type.");

 if (m == NULL)

  return;

 Py_INCREF(&NoddyType);

 PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);

}

To provide greater control, over the firstand lastattributes, we’ll use custom getter and setter functions. Here are the functions for getting and setting the firstattribute:

为给first和last属性提供更好的控制,我们将使用定制的getter和setter函数。下面是获取和设置first属性的函数:

Noddy_getfirst(Noddy *self, void *closure)

{

 Py_INCREF(self->first);

 return self->first;

}

static int

Noddy_setfirst(Noddy *self, PyObject *value, void *closure)

{

 if (value == NULL) {

 PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");

  return -1;

 }

 if (! PyString_Check(value)) {

  PyErr_SetString(PyExc_TypeError,"The first attribute value must be a string");

  return -1;

 }

 Py_DECREF(self->first);

 Py_INCREF(value);

 self->first = value;

 return 0;

}

The getter function is passed a Noddyobject and a “closure”, which is 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.)

getter函数传入一个Noddy对象和一个“闭包”,闭包是一个空指针,在这里忽略闭包。(闭包支持传入getter和setter函数的数据定义的一个高级用法。例如,能基于闭包中的数据,来允许决定属性的一系列getter和setter函数获取或设置属性。)

The setter function is passed the Noddyobject, the new value, and the closure. The new value may be NULL, in which case the attribute is being deleted. In our setter, we raise an error if the attribute is deleted or if the attribute value is not a string.

setter函数传入Noddy对象、新值和闭包。新值可能为NULL,那种情况下说明属性已被删除,在我们的setter中,若属性被删除或不为字符串时,我们会抛出一个错误。

We create an array of PyGetSetDefstructures:

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

static PyGetSetDef Noddy_getseters[] = {

 {"first",(getter)Noddy_getfirst, (setter)Noddy_setfirst,"first name",NULL},

 {"last",(getter)Noddy_getlast, (setter)Noddy_setlast,"last name",NULL},

 {NULL} /* Sentinel */

};

and register it in the tp_getsetslot:

并在tp_getset域注册:

Noddy_getseters, /* tp_getset */

to register out attribute getters and setters.

以注册外部属性getters和setters。

The last item in a PyGetSetDefstructure is the closure mentioned above. In this case, we aren’t using the closure, so we just pass NULL.

PyGetSetDef结构体中的最后一项是上面提到的闭包。在这里,我们没有使用闭包,所以就传入NULL。

We also remove the member definitions for these attributes:

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

static PyMemberDef Noddy_members[] = {

 {"number", T_INT, offsetof(Noddy, number), 0,"noddy number"},

 {NULL} /* Sentinel */

};

We also need to update the tp_inithandler to only allow strings3to be passed:

我们还需要更新tp_init句柄,使其只允许字符串传入。

—————————————————————————————————–

3We 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 out objects.

3现在我们知道名和姓都为字符串,或许,我们不必太注意减少他们的引用计数,但是,我们接收的可能是字符串的子类。尽管释放一个标准的字符串不会回调进入我们的对象,但我们不能确保释放字符串子类的实例时不会这样。

 

static int

Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)

{

 PyObject *first=NULL, *last=NULL, *tmp;

 static char *kwlist[] = {"first", "last", "number", NULL};

 if (! PyArg_ParseTupleAndKeywords(args, kwds, "|SSi", 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;

}

With these changes, we can assure that the firstand lastmembers are never NULLso we can remove checks for NULL values in almost all cases.This means that most of thePy_XDECREF()calls can be converted to Py_- DECREF()calls. The only place we can’t change these calls is in the deallocator, where there is the possibility that the initialization of these members failed in the constructor.

经过这些修改,我们可以确信成员first和last永远不会为NULL,所以我们可以删除几乎全部的NULL值检查。这意味着大部分的Py_XDECREF()调用可转换为Py_DECREF()调用。唯一我们不能改变这些调用的地方是是析构函数(deallocator),因为在构造函数中,这些成员的初始化还是有失败的可能。

We also rename the module initialization function and module name in the initialization function, as we did before,

and we add an extra definition to the ‘setup.py’ file.

我们还重命名了模块初始化函数以及初始化函数中模块的名字,就像我们以前做的那样,同时我们也在‘setup.py’文件中增加了额外的定义。

2.1.3 Supporting cyclic garbage collection 支持循环垃圾收集

Python has a cyclic-garbage collector that can identify unneeded objects even when their reference counts are not zero. This can happen when objects are involved in cycles. For example, consider:

Python有一个循环垃圾收集器,能识别不再需要的对象,即使引用计数不为零。例如,考虑下面的情况:

>>> l = []

>>> l.append(l)

>>> del l

In this example, we create a list that contains itself. When we delete it, it still has a reference from itself. Its reference count doesn’t drop to zero. Fortunately, Python’s cyclic-garbage collector will eventually figure outthat the list is garbage and free it.

在这个例子中,我们创建了一个包含自身的列表。当我们删除它时,它还有一个对自身的引用。它的引用计数不会降为零。幸运的是,Python的循环垃圾收集器最终会知道该列表是垃圾并释放它。

In the second version of the Noddyexample, we allowed any kind of object to be stored in thefirstor lastattributes.4This means that Noddyobjects can participate in cycles:

在第二个版本的Noddy示例中,我们允许任何类型对象存储于first或last属性中。这意味着Noddy对象能参与进循环。

——————————————————————————————————

4Even in the third version, we aren’t guaranteed to avoid cycles. Instances of string subclasses are allowed and string subclasses couldallow cycles even if normal strings don’t.

 

即使在第三个版本中,我们也确保不了能避免循环。string子类的实例是被允许的而且还容许循环,尽管正常的string不会。

>>> import noddy2

>>> n = noddy2.Noddy()

>>> l = [n]

>>> n.first = l

This is pretty silly, but it gives us an excuse to add support for the cyclic-garbage collector to theNoddyexample. To support cyclic garbage collection, types need to fill two slots and set a class flag that enables these slots:

这样做相当愚蠢,但却给了我们为Noddy示例添加循环垃圾收集器的一个理由。为支持循环垃圾收集,类型需要填充两个域和设置一个使这些域可用的类标识:

#include <Python.h>

#include "structmember.h"

typedef struct {

 PyObject_HEAD

 PyObject *first;

 PyObject *last;

 int number;

} Noddy;

static int

Noddy_traverse(Noddy *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;

}

static int

Noddy_clear(Noddy *self)

{

 PyObject *tmp;

 tmp = self->first;

 self->first = NULL;

 Py_XDECREF(tmp);

 tmp = self->last;

 self->last = NULL;

 Py_XDECREF(tmp);

 return 0;

}

static void

Noddy_dealloc(Noddy* self)

{

 Noddy_clear(self);

 self->ob_type->tp_free((PyObject*)self);

}

static PyObject *

Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)

{

 Noddy *self;

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

 if (self != NULL) {

 self->first = PyString_FromString("");

 if (self->first == NULL)

 {

  Py_DECREF(self);

  return NULL;

 }

 self->last = PyString_FromString("");

 if (self->last == NULL)

 {

  Py_DECREF(self);

  return NULL;

 }

 self->number = 0;

 }

 return (PyObject *)self;

}

static int

Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)

{

 PyObject *first=NULL, *last=NULL, *tmp;

 static char *kwlist[] = {"first", "last", "number", NULL};

 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 Noddy_members[] = {

 {"first", T_OBJECT_EX, offsetof(Noddy, first), 0,"first name"},

 {"last", T_OBJECT_EX, offsetof(Noddy, last), 0,"last name"},

 {"number", T_INT, offsetof(Noddy, number), 0,"noddy number"},

 {NULL} /* Sentinel */

};

static PyObject *

Noddy_name(Noddy* self)

{

 static PyObject *format = NULL;

 PyObject *args, *result;

 if (format == NULL) {

  format = PyString_FromString("%s %s");

  if (format == NULL)

  return NULL;

 }

 if (self->first == NULL) {

  PyErr_SetString(PyExc_AttributeError, "first");

  return NULL;

 }

 if (self->last == NULL) {

  PyErr_SetString(PyExc_AttributeError, "last");

  return NULL;

 }

 args = Py_BuildValue("OO", self->first, self->last);

 if (args == NULL)

  return NULL;

 result = PyString_Format(format, args);

 Py_DECREF(args);

 return result;

}

static PyMethodDef Noddy_methods[] = {

 {"name", (PyCFunction)Noddy_name, METH_NOARGS,"Return the name, combining the first and last name"},

 {NULL} /* Sentinel */

};

static PyTypeObject NoddyType = {

 PyObject_HEAD_INIT(NULL)

 0,                              /*ob_size*/

 "noddy.Noddy",                /*tp_name*/

 sizeof(Noddy),                /*tp_basicsize*/

 0,                              /*tp_itemsize*/

 (destructor)Noddy_dealloc, /*tp_dealloc*/

 0,                              /*tp_print*/

 0,                              /*tp_getattr*/

 0,                              /*tp_setattr*/

 0,                              /*tp_compare*/

 0,                              /*tp_repr*/

 0,                              /*tp_as_number*/

 0,                              /*tp_as_sequence*/

 0,                              /*tp_as_mapping*/

 0,                              /*tp_hash */

 0,                              /*tp_call*/

 0,                              /*tp_str*/

 0,                              /*tp_getattro*/

 0,                              /*tp_setattro*/

 0,                              /*tp_as_buffer*/

 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/

 "Noddy objects",             /* tp_doc */

 (traverseproc)Noddy_traverse,     /* tp_traverse */

 (inquiry)Noddy_clear,               /* tp_clear */

 0,                                      /* tp_richcompare */

 0,                                      /* tp_weaklistoffset */

 0,                                      /* tp_iter */

 0,                                      /* tp_iternext */

 Noddy_methods,                        /* tp_methods */

 Noddy_members,                        /* tp_members */

 0,                                       /* tp_getset */

 0,                                       /* tp_base */

 0,                                       /* tp_dict */

 0,                                       /* tp_descr_get */

 0,                                       /* tp_descr_set */

 0,                                       /* tp_dictoffset */

 (initproc)Noddy_init,                /* tp_init */

 0,                                       /* tp_alloc */

 Noddy_new,                              /* tp_new */

 };

static PyMethodDef module_methods[] = {

 {NULL} /* Sentinel */

};

#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */

#define PyMODINIT_FUNC void

#endif

PyMODINIT_FUNC

initnoddy4(void)

{

 PyObject* m;

 if (PyType_Ready(&NoddyType) < 0)

  return;

 m = Py_InitModule3("noddy4", module_methods,"Example module that creates an extension type.");

 if (m == NULL)

  return;

 Py_INCREF(&NoddyType);

 PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);

}

The traversal method provides access to subobjects that could participate in cycles:

traversal方法提供了对参与到循环中的子对象的访问:

static int

Noddy_traverse(Noddy *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;

}

For each subobject that can participate in cycles, we need to call the visit()function, which is passed to the traversal method. The visit()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.

对每个参与到循环中的子对象,我们都要调用被传入traversal方法的visit()函数。visit()函数以传入traversal方法的子对象(self)以及附加参数arg为参数。

Python 2.4 and higher provide a Py_VISIT()macro that automates calling visit functions. WithPy_VISIT(), Noddy_traverse()can be simplified:

Python 2.4以及更高版本提供了一个宏Py_VISIT(),可以自动调用visit函数。有了Py_VISIT(),Noddy_ traverse()能被简化为:

static int

Noddy_traverse(Noddy *self, visitproc visit, void *arg)

{

 Py_VISIT(self->first);

 Py_VISIT(self->last);

 return 0;

}

Note:Note that the tp_traverseimplementation must name its arguments exactly visitandargin order to use Py_VISIT(). This is to encourage uniformity across these boring implementations.

注意:注意到为了使用Py_VISIT(),域tp_traverse的实现中必须准确地命名visitarg。这是为了在这些烦人的实现间保持一致。

We also need to provide a method for clearing any subobjects that can participate in cycles. We implement the method and reimplement the deallocator to use it:

为了清理参与到循环中的任意子对象,我们还要提供一个清理方法。我们实现了这个方法,并重新实现析构方法以使用清理方法。

static int

Noddy_clear(Noddy *self)

{   

 PyObject *tmp;

 tmp = self->first;

 self->first = NULL;

 Py_XDECREF(tmp);

 tmp = self->last;

 self->last = NULL;

 Py_XDECREF(tmp);

 return 0;

}

static void

Noddy_dealloc(Noddy* self)

{

 Noddy_clear(self);

 self->ob_type->tp_free((PyObject*)self);

}

Notice the use of a temporary variable in Noddy_clear(). We use the temporary variable so that we can set each member to NULLbefore decrementing its reference count. We do this because, as was discussed earlier, if the reference count drops to zero, we might cause code to run that calls back into the object. In addition, because we now support garbage collection, we also have to worry about code being run that triggers garbage collection. If garbage collection is run, ourtp_traversehandler could get called. We can’t take a chance of havingNoddy_traverse()called when a member’s reference count has dropped to zero and its value hasn’t been set to NULL.

注意在Noddy_clear()中临时变量的使用。我们使用临时变量以便我们能在减少成员的引用计数之前将成员设置为NULL。我们这样做的原因,正如前面讨论过的,如果引用计数降为零,可能会引发代码运行回调到对象中。此外,由于我们现在还支持垃圾收集,我们还需要关心会触发垃圾收集的代码。如果垃圾收集机制在运行,我们的tp_traverse句柄可能会被调用。当成员的引用计数已降为0但它的值却还未设置为NULL时,我们不能冒险让Noddy_traverse()被调用。

Python 2.4 and higher provide a Py_CLEAR() that automates the careful decrementing of reference counts. With Py_CLEAR(), the Noddy_clear()function can be simplified:

Python 2.4以及更高版本提供了一个宏Py_CLEAR(),可以使谨慎的引用计数减少自动完成。有了Py_CLEA R(),Noddy_clear()函数可以简化为:

static int

Noddy_clear(Noddy *self)

{

 Py_CLEAR(self->first);

 Py_CLEAR(self->last);

return 0;

}

Finally, we add the Py_TPFLAGS_HAVE_GCflag to the class flags:

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

Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/

That’s pretty much it. If we had written custom tp_allocor tp_freeslots, we’d need to modify them for cyclic-garbage collection. Most extensions will use the versions automatically provided.

到这就差不多了,如果我们还定制了tp_alloc和tp_free域,我们就需要为垃圾收集修改它们。大多数扩展会使用自动提供的版本。

No comments:

Post a Comment