<p>除了引用计数错误外,您的扩展函数中还有更多问题,更多问题如下:</p>
<hr/>
<p>虽然带有正确引用计数的<code>PyList_SetItem</code>是首选方法,但(难看的)选项是使用<a href="https://docs.python.org/3/c-api/list.html#c.PyList_SET_ITEM" rel="nofollow">^{<cd2>}</a>宏,它可以避免执行递增:</p>
<blockquote>
<p><code>void PyList_SET_ITEM(PyObject *list, Py_ssize_t i, PyObject *o)</code></p>
<p>Macro form of <code>PyList_SetItem()</code> without error checking. This is normally only used to fill in new lists where there is no previous
content.</p>
<p><strong>Note</strong></p>
<p>This macro “steals” a reference to item, and, <em>unlike <code>PyList_SetItem()</code>, does not discard a reference to any item that is
being replaced; any reference in list at position <code>i</code> will be leaked</em>.</p>
</blockquote>
<p>因此,<code>PyList_SET_ITEM</code>既不增加也不减少任何引用计数器,这适合我们,因为元素最初和最后都在同一个列表中。在</p>
<pre><code>inline void _List_SwapItems(PyObject* list, Py_ssize_t i1, Py_ssize_t i2){
PyObject* tmp = PyList_GET_ITEM(list, i2);
PyList_SET_ITEM(list, i2, PyList_GET_ITEM(list, i1));
PyList_SET_ITEM(list, i1, tmp);
}
</code></pre>
<p>请注意,这根本不做任何错误检查,因此您需要确保您的索引在边界内(这是<code>for</code>循环负责的)。在</p>
<hr/>
<p>您的代码还有一个尚未讨论的坏问题—完全缺乏错误检查。例如,当传入一个非列表对象时,应该引发一个<code>TypeError</code>。现在代码将在<a href="https://hg.python.org/cpython/file/3.4/Objects/listobject.c#l182" rel="nofollow">^{<cd6>}</a>失败,返回-1并设置一个内部异常,这可能导致未来所有C扩展的错误行为:</p>
<p>同样,如果传入的参数数目不正确,<a href="https://docs.python.org/3/c-api/arg.html#c.PyArg_ParseTuple" rel="nofollow">^{<cd7>}</a>can和<strong>将失败,因此您必须检查其返回值;在这种情况下,<code>list</code>可能未初始化,并且您的代码将具有完全未定义的行为。在</p>
<p>C-API文档<a href="https://docs.python.org/3/c-api/exceptions.html#exception-handling" rel="nofollow">states the following</a>:</p>
<blockquote>
<p>When a function must fail because some function it called failed, it
generally doesn’t set the error indicator; the function it called
already set it. It is responsible for either handling the error and
clearing the exception or returning after cleaning up any resources it
holds (such as object references or memory allocations); <strong>it should not
continue normally if it is not prepared to handle the error. If
returning due to an error, it is important to indicate to the caller
that an error has been set. If the error is not handled or carefully
propagated, additional calls into the Python/C API may not behave as
intended and may fail in mysterious ways.</strong></p>
</blockquote>
<p>因此,以下是编写扩展函数的正确方法:</p>
^{pr2}$