1017 lines
152 KiB
HTML
1017 lines
152 KiB
HTML
|
||
<!DOCTYPE html>
|
||
|
||
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh_CN">
|
||
<head>
|
||
<meta charset="utf-8" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
|
||
|
||
<title>1. 使用 C 或 C++ 扩展 Python — Python 3.8.20 文档</title><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
||
<link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
||
|
||
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
|
||
<script src="../_static/jquery.js"></script>
|
||
<script src="../_static/underscore.js"></script>
|
||
<script src="../_static/doctools.js"></script>
|
||
<script src="../_static/language_data.js"></script>
|
||
<script src="../_static/translations.js"></script>
|
||
|
||
<script src="../_static/sidebar.js"></script>
|
||
|
||
<link rel="search" type="application/opensearchdescription+xml"
|
||
title="在 Python 3.8.20 文档 中搜索"
|
||
href="../_static/opensearch.xml"/>
|
||
<link rel="author" title="关于这些文档" href="../about.html" />
|
||
<link rel="index" title="索引" href="../genindex.html" />
|
||
<link rel="search" title="搜索" href="../search.html" />
|
||
<link rel="copyright" title="版权所有" href="../copyright.html" />
|
||
<link rel="next" title="2. 自定义扩展类型:教程" href="newtypes_tutorial.html" />
|
||
<link rel="prev" title="扩展和嵌入 Python 解释器" href="index.html" />
|
||
<link rel="canonical" href="https://docs.python.org/3/extending/extending.html" />
|
||
|
||
|
||
|
||
|
||
|
||
<style>
|
||
@media only screen {
|
||
table.full-width-table {
|
||
width: 100%;
|
||
}
|
||
}
|
||
</style>
|
||
<link rel="shortcut icon" type="image/png" href="../_static/py.svg" />
|
||
<script type="text/javascript" src="../_static/copybutton.js"></script>
|
||
<script type="text/javascript" src="../_static/menu.js"></script>
|
||
|
||
</head>
|
||
<body>
|
||
<div class="mobile-nav">
|
||
<input type="checkbox" id="menuToggler" class="toggler__input" aria-controls="navigation"
|
||
aria-pressed="false" aria-expanded="false" role="button" aria-label="Menu" />
|
||
<label for="menuToggler" class="toggler__label">
|
||
<span></span>
|
||
</label>
|
||
<nav class="nav-content" role="navigation">
|
||
<a href="https://www.python.org/" class="nav-logo">
|
||
<img src="../_static/py.svg" alt="Logo"/>
|
||
</a>
|
||
<div class="version_switcher_placeholder"></div>
|
||
<form role="search" class="search" action="../search.html" method="get">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="search-icon">
|
||
<path fill-rule="nonzero"
|
||
d="M15.5 14h-.79l-.28-.27a6.5 6.5 0 001.48-5.34c-.47-2.78-2.79-5-5.59-5.34a6.505 6.505 0 00-7.27 7.27c.34 2.8 2.56 5.12 5.34 5.59a6.5 6.5 0 005.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" fill="#444"></path>
|
||
</svg>
|
||
<input type="text" name="q" aria-label="快速搜索"/>
|
||
<input type="submit" value="转向"/>
|
||
</form>
|
||
</nav>
|
||
<div class="menu-wrapper">
|
||
<nav class="menu" role="navigation" aria-label="main navigation">
|
||
<div class="language_switcher_placeholder"></div>
|
||
<h3><a href="../contents.html">目录</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">1. 使用 C 或 C++ 扩展 Python</a><ul>
|
||
<li><a class="reference internal" href="#a-simple-example">1.1. 一个简单的例子</a></li>
|
||
<li><a class="reference internal" href="#intermezzo-errors-and-exceptions">1.2. 关于错误和异常</a></li>
|
||
<li><a class="reference internal" href="#back-to-the-example">1.3. 回到例子</a></li>
|
||
<li><a class="reference internal" href="#the-module-s-method-table-and-initialization-function">1.4. 模块方法表和初始化函数</a></li>
|
||
<li><a class="reference internal" href="#compilation-and-linkage">1.5. 编译和链接</a></li>
|
||
<li><a class="reference internal" href="#calling-python-functions-from-c">1.6. 在C中调用Python函数</a></li>
|
||
<li><a class="reference internal" href="#extracting-parameters-in-extension-functions">1.7. 提取扩展函数的参数</a></li>
|
||
<li><a class="reference internal" href="#keyword-parameters-for-extension-functions">1.8. 给扩展函数的关键字参数</a></li>
|
||
<li><a class="reference internal" href="#building-arbitrary-values">1.9. 构造任意值</a></li>
|
||
<li><a class="reference internal" href="#reference-counts">1.10. 引用计数</a><ul>
|
||
<li><a class="reference internal" href="#reference-counting-in-python">1.10.1. Python中的引用计数</a></li>
|
||
<li><a class="reference internal" href="#ownership-rules">1.10.2. 拥有规则</a></li>
|
||
<li><a class="reference internal" href="#thin-ice">1.10.3. 危险的薄冰</a></li>
|
||
<li><a class="reference internal" href="#null-pointers">1.10.4. NULL指针</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#writing-extensions-in-c">1.11. 在C++中编写扩展</a></li>
|
||
<li><a class="reference internal" href="#providing-a-c-api-for-an-extension-module">1.12. 给扩展模块提供C API</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<h4>上一个主题</h4>
|
||
<p class="topless"><a href="index.html"
|
||
title="上一章">扩展和嵌入 Python 解释器</a></p>
|
||
<h4>下一个主题</h4>
|
||
<p class="topless"><a href="newtypes_tutorial.html"
|
||
title="下一章"><span class="section-number">2. </span>自定义扩展类型:教程</a></p>
|
||
<div role="note" aria-label="source link">
|
||
<h3>本页</h3>
|
||
<ul class="this-page-menu">
|
||
<li><a href="../bugs.html">报告 Bug</a></li>
|
||
<li>
|
||
<a href="https://github.com/python/cpython/blob/3.8/Doc/extending/extending.rst"
|
||
rel="nofollow">显示源代码
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="outdated-warning" style="padding: .5em; text-align: center; background-color: #FFBABA; color: #6A0E0E;">
|
||
这个文档所针对的是一个已不再受支持的 Python 旧版本。
|
||
你应当升级版本,并阅读
|
||
<a href="/3/extending/extending.html"> Python 当前稳定版本的文档</a>.
|
||
</div>
|
||
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>导航</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="总目录"
|
||
accesskey="I">索引</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python 模块索引"
|
||
>模块</a> |</li>
|
||
<li class="right" >
|
||
<a href="newtypes_tutorial.html" title="2. 自定义扩展类型:教程"
|
||
accesskey="N">下一页</a> |</li>
|
||
<li class="right" >
|
||
<a href="index.html" title="扩展和嵌入 Python 解释器"
|
||
accesskey="P">上一页</a> |</li>
|
||
|
||
<li><img src="../_static/py.svg" alt="python logo" style="vertical-align: middle; margin-top: -1px"/></li>
|
||
<li><a href="https://www.python.org/">Python</a> »</li>
|
||
<li class="switchers">
|
||
<div class="language_switcher_placeholder"></div>
|
||
<div class="version_switcher_placeholder"></div>
|
||
</li>
|
||
<li>
|
||
|
||
</li>
|
||
<li id="cpython-language-and-version">
|
||
<a href="../index.html">3.8.20 Documentation</a> »
|
||
</li>
|
||
|
||
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">扩展和嵌入 Python 解释器</a> »</li>
|
||
<li class="right">
|
||
|
||
|
||
<div class="inline-search" role="search">
|
||
<form class="inline-search" action="../search.html" method="get">
|
||
<input placeholder="快速搜索" aria-label="快速搜索" type="text" name="q" />
|
||
<input type="submit" value="转向" />
|
||
<input type="hidden" name="check_keywords" value="yes" />
|
||
<input type="hidden" name="area" value="default" />
|
||
</form>
|
||
</div>
|
||
|
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="document">
|
||
<div class="documentwrapper">
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<section id="extending-python-with-c-or-c">
|
||
<span id="extending-intro"></span><h1><span class="section-number">1. </span>使用 C 或 C++ 扩展 Python<a class="headerlink" href="#extending-python-with-c-or-c" title="永久链接至标题">¶</a></h1>
|
||
<p>如果你会用 C,添加新的 Python 内置模块会很简单。以下两件不能用 Python 直接做的事,可以通过 <em class="dfn">extension modules</em> 来实现:实现新的内置对象类型;调用 C 的库函数和系统调用。</p>
|
||
<p>为了支持扩展,Python API(应用程序编程接口)定义了一系列函数、宏和变量,可以访问 Python 运行时系统的大部分内容。Python 的 API 可以通过在一个 C 源文件中引用 <code class="docutils literal notranslate"><span class="pre">"Python.h"</span></code> 头文件来使用。</p>
|
||
<p>扩展模块的编写方式取决与你的目的以及系统设置;下面章节会详细介绍。</p>
|
||
<div class="admonition note">
|
||
<p class="admonition-title">注解</p>
|
||
<p>C扩展接口特指CPython,扩展模块无法在其他Python实现上工作。在大多数情况下,应该避免写C扩展,来保持可移植性。举个例子,如果你的用例调用了C库或系统调用,你应该考虑使用 <a class="reference internal" href="../library/ctypes.html#module-ctypes" title="ctypes: A foreign function library for Python."><code class="xref py py-mod docutils literal notranslate"><span class="pre">ctypes</span></code></a> 模块或 <a class="reference external" href="https://cffi.readthedocs.io/">cffi</a> 库,而不是自己写C代码。这些模块允许你写Python代码来接口C代码,而且可移植性更好。不知为何编译失败了。</p>
|
||
</div>
|
||
<section id="a-simple-example">
|
||
<span id="extending-simpleexample"></span><h2><span class="section-number">1.1. </span>一个简单的例子<a class="headerlink" href="#a-simple-example" title="永久链接至标题">¶</a></h2>
|
||
<p>让我们创建一个扩展模块 <code class="docutils literal notranslate"><span class="pre">spam</span></code> (Monty Python 粉丝最喜欢的食物...) 并且想要创建对应 C 库函数 <code class="xref c c-func docutils literal notranslate"><span class="pre">system()</span></code> <a class="footnote-reference brackets" href="#id5" id="id1">1</a> 的 Python 接口。 这个函数接受一个以 null 结尾的字符串参数并返回一个整数。 我们希望可以在 Python 中以如下方式调用此函数:</p>
|
||
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">spam</span>
|
||
<span class="gp">>>> </span><span class="n">status</span> <span class="o">=</span> <span class="n">spam</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s2">"ls -l"</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>首先创建一个 <code class="file docutils literal notranslate"><span class="pre">spammodule.c</span></code> 文件。(传统上,如果一个模块叫 <code class="docutils literal notranslate"><span class="pre">spam</span></code>,则对应实现它的 C 文件叫 <code class="file docutils literal notranslate"><span class="pre">spammodule.c</span></code>;如果这个模块名字非常长,比如 <code class="docutils literal notranslate"><span class="pre">spammify</span></code>,则这个模块的文件可以直接叫 <code class="file docutils literal notranslate"><span class="pre">spammify.c</span></code>。)</p>
|
||
<p>文件中开始的两行是:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cp">#define PY_SSIZE_T_CLEAN</span>
|
||
<span class="cp">#include</span><span class="w"> </span><span class="cpf"><Python.h></span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这会导入 Python API(如果你喜欢,你可以在这里添加描述模块目标和版权信息的注释)。</p>
|
||
<div class="admonition note">
|
||
<p class="admonition-title">注解</p>
|
||
<p>由于 Python 可能会定义一些能在某些系统上影响标准头文件的预处理器定义,因此在包含任何标准头文件之前,你 <em>必须</em> 先包含 <code class="file docutils literal notranslate"><span class="pre">Python.h</span></code>。</p>
|
||
<p>推荐总是在 <code class="docutils literal notranslate"><span class="pre">Python.h</span></code> 前定义 <code class="docutils literal notranslate"><span class="pre">PY_SSIZE_T_CLEAN</span></code> 。查看 <a class="reference internal" href="#parsetuple"><span class="std std-ref">提取扩展函数的参数</span></a> 来了解这个宏的更多内容。</p>
|
||
</div>
|
||
<p>所有在 <code class="file docutils literal notranslate"><span class="pre">Python.h</span></code> 中定义的用户可见的符号都具有 <code class="docutils literal notranslate"><span class="pre">Py</span></code> 或 <code class="docutils literal notranslate"><span class="pre">PY</span></code> 前缀,已在标准头文件中定义的那些除外。 考虑到便利性,也由于其在 Python 解释器中被广泛使用,<code class="docutils literal notranslate"><span class="pre">"Python.h"</span></code> 还包含了一些标准头文件: <code class="docutils literal notranslate"><span class="pre"><stdio.h></span></code>,<code class="docutils literal notranslate"><span class="pre"><string.h></span></code>,<code class="docutils literal notranslate"><span class="pre"><errno.h></span></code> 和 <code class="docutils literal notranslate"><span class="pre"><stdlib.h></span></code>。 如果后面的头文件在你的系统上不存在,它还会直接声明函数 <code class="xref c c-func docutils literal notranslate"><span class="pre">malloc()</span></code>,<code class="xref c c-func docutils literal notranslate"><span class="pre">free()</span></code> 和 <code class="xref c c-func docutils literal notranslate"><span class="pre">realloc()</span></code>。</p>
|
||
<p>下面添加C函数到扩展模块,当调用 <code class="docutils literal notranslate"><span class="pre">spam.system(string)</span></code> 时会做出响应,(我们稍后会看到调用):</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span>
|
||
<span class="nf">spam_system</span><span class="p">(</span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">command</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">sts</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="s">"s"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">command</span><span class="p">))</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="n">sts</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">system</span><span class="p">(</span><span class="n">command</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">PyLong_FromLong</span><span class="p">(</span><span class="n">sts</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>有个直接翻译参数列表的方法(举个例子,单独的 <code class="docutils literal notranslate"><span class="pre">"ls</span> <span class="pre">-l"</span></code> )到要传递给C函数的参数。C函数总是有两个参数,通常名字是 <em>self</em> 和 <em>args</em> 。</p>
|
||
<p>对模块级函数, <em>self</em> 参数指向模块对象;对于方法则指向对象实例。</p>
|
||
<p><em>args</em> 参数是指向一个 Python 的 tuple 对象的指针,其中包含参数。 每个 tuple 项对应一个调用参数。 这些参数也全都是 Python 对象 --- 要在我们的 C 函数中使用它们就需要先将其转换为 C 值。 Python API 中的函数 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 会检查参数类型并将其转换为 C 值。 它使用模板字符串确定需要的参数类型以及存储被转换的值的 C 变量类型。 细节将稍后说明。</p>
|
||
<p><a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 在所有参数都有正确类型且组成部分按顺序放在传递进来的地址里时,返回真(非零)。其在传入无效参数时返回假(零)。在后续例子里,还会抛出特定异常,使得调用的函数可以理解返回 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> (也就是例子里所见)。</p>
|
||
</section>
|
||
<section id="intermezzo-errors-and-exceptions">
|
||
<span id="extending-errors"></span><h2><span class="section-number">1.2. </span>关于错误和异常<a class="headerlink" href="#intermezzo-errors-and-exceptions" title="永久链接至标题">¶</a></h2>
|
||
<p>在 Python 解释器中有一个重要的惯例:当一个函数出错时,它应当设置异常条件并返回错误值(通常为 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 指针)。 异常存储于解释器内部的静态全局变量中;如此变量为 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 表示未发生异常。 还有第二个全局变量用于保存异常的“关联值”(即 <a class="reference internal" href="../reference/simple_stmts.html#raise"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">raise</span></code></a> 的第二个参数)。 第三个变量包含 Python 代码产生错误情况下的栈回溯信息。 这三个变量是 Python 中 <a class="reference internal" href="../library/sys.html#sys.exc_info" title="sys.exc_info"><code class="xref py py-meth docutils literal notranslate"><span class="pre">sys.exc_info()</span></code></a> 的结果在 C 中的对应物(请参阅 Python 库参考的 <a class="reference internal" href="../library/sys.html#module-sys" title="sys: Access system-specific parameters and functions."><code class="xref py py-mod docutils literal notranslate"><span class="pre">sys</span></code></a> 模块部分)。 了解它们对于理解错误的传递方式是非常重要的。</p>
|
||
<p>Python API中定义了一些函数来设置这些变量。</p>
|
||
<p>最常用的就是 <a class="reference internal" href="../c-api/exceptions.html#c.PyErr_SetString" title="PyErr_SetString"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_SetString()</span></code></a>。 其参数是异常对象和 C 字符串。 异常对象一般是像 <code class="xref c c-data docutils literal notranslate"><span class="pre">PyExc_ZeroDivisionError</span></code> 这样的预定义对象。 C 字符串指明异常原因,并被转换为一个 Python 字符串对象存储为异常的“关联值”。</p>
|
||
<p>另一个有用的函数是 <a class="reference internal" href="../c-api/exceptions.html#c.PyErr_SetFromErrno" title="PyErr_SetFromErrno"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_SetFromErrno()</span></code></a> ,仅接受一个异常对象,异常描述包含在全局变量 <code class="xref c c-data docutils literal notranslate"><span class="pre">errno</span></code> 中。最通用的函数还是 <a class="reference internal" href="../c-api/exceptions.html#c.PyErr_SetObject" title="PyErr_SetObject"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_SetObject()</span></code></a> ,包含两个参数,分别为异常对象和异常描述。你不需要使用 <a class="reference internal" href="../c-api/refcounting.html#c.Py_INCREF" title="Py_INCREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_INCREF()</span></code></a> 来增加传递到其他函数的参数对象的引用计数。</p>
|
||
<p>你可以通过 <a class="reference internal" href="../c-api/exceptions.html#c.PyErr_Occurred" title="PyErr_Occurred"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_Occurred()</span></code></a> 在不造成破坏的情况下检测是否设置了异常。 这将返回当前异常对象,或者如果未发生异常则返回 <code class="docutils literal notranslate"><span class="pre">NULL</span></code>。 你通常不需要调用 <a class="reference internal" href="../c-api/exceptions.html#c.PyErr_Occurred" title="PyErr_Occurred"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_Occurred()</span></code></a> 来查看函数调用中是否发生了错误,因为你应该能从返回值中看出来。</p>
|
||
<p>当一个函数 <em>f</em> 调用另一个函数 <em>g</em> 时检测到后者出错了,<em>f</em> 应当自己返回一个错误值 (通常为 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 或 <code class="docutils literal notranslate"><span class="pre">-1</span></code>)。 它 <em>不应该</em> 调用某个 <code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_*()</span></code> 函数 --- 这类函数已经被 <em>g</em> 调用过了。 <em>f</em> 的调用者随后也应当返回一个错误来提示 <em>它的</em> 调用者,同样 <em>不应该</em> 调用 <code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_*()</span></code>,依此类推 --- 错误的最详细原因已经由首先检测到它的函数报告了。 一旦这个错误到达 Python 解释器的主循环,它会中止当前执行的 Python 代码并尝试找出由 Python 程序员所指定的异常处理程序。</p>
|
||
<p>(在某些情况下,当模块确实能够通过调用其它 <code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_*()</span></code> 函数给出更加详细的错误消息,并且在这些情况是可以这样做的。 但是按照一般规则,这是不必要的,并可能导致有关错误原因的信息丢失:大多数操作会由于种种原因而失败。)</p>
|
||
<p>想要忽略由一个失败的函数调用所设置的异常,异常条件必须通过调用 <a class="reference internal" href="../c-api/exceptions.html#c.PyErr_Clear" title="PyErr_Clear"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_Clear()</span></code></a> 显式地被清除。 C 代码应当调用 <a class="reference internal" href="../c-api/exceptions.html#c.PyErr_Clear" title="PyErr_Clear"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_Clear()</span></code></a> 的唯一情况是如果它不想将错误传给解释器而是想完全由自己来处理它(可能是尝试其他方法,或是假装没有出错)。</p>
|
||
<p>每次失败的 <code class="xref c c-func docutils literal notranslate"><span class="pre">malloc()</span></code> 调用必须转换为一个异常。 <code class="xref c c-func docutils literal notranslate"><span class="pre">malloc()</span></code> (或 <code class="xref c c-func docutils literal notranslate"><span class="pre">realloc()</span></code> )的直接调用者必须调用 <a class="reference internal" href="../c-api/exceptions.html#c.PyErr_NoMemory" title="PyErr_NoMemory"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_NoMemory()</span></code></a> 来返回错误来提示。所有对象创建函数(例如 <a class="reference internal" href="../c-api/long.html#c.PyLong_FromLong" title="PyLong_FromLong"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyLong_FromLong()</span></code></a> )已经这么做了,所以这个提示仅用于直接调用 <code class="xref c c-func docutils literal notranslate"><span class="pre">malloc()</span></code> 的情况。</p>
|
||
<p>还要注意的是,除了 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 等重要的例外,返回整数状态码的函数通常都是返回正值或零来表示成功,而以 <code class="docutils literal notranslate"><span class="pre">-1</span></code> 表示失败,如同 Unix 系统调用一样。</p>
|
||
<p>最后,当你返回一个错误指示器时要注意清理垃圾(通过为你已经创建的对象执行 <a class="reference internal" href="../c-api/refcounting.html#c.Py_XDECREF" title="Py_XDECREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_XDECREF()</span></code></a> 或 <a class="reference internal" href="../c-api/refcounting.html#c.Py_DECREF" title="Py_DECREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_DECREF()</span></code></a> 调用)!</p>
|
||
<p>选择引发哪个异常完全取决于你的喜好。 所有内置的 Python 异常都有对应的预声明 C 对象,例如 <code class="xref c c-data docutils literal notranslate"><span class="pre">PyExc_ZeroDivisionError</span></code>,你可以直接使用它们。 当然,你应当明智地选择异常 --- 不要使用 <code class="xref c c-data docutils literal notranslate"><span class="pre">PyExc_TypeError</span></code> 来表示一个文件无法被打开 (那大概应该用 <code class="xref c c-data docutils literal notranslate"><span class="pre">PyExc_IOError</span></code>)。 如果参数列表有问题,<a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 函数通常会引发 <code class="xref c c-data docutils literal notranslate"><span class="pre">PyExc_TypeError</span></code>。 如果你想要一个参数的值必须处于特定范围之内或必须满足其他条件,则适宜使用 <code class="xref c c-data docutils literal notranslate"><span class="pre">PyExc_ValueError</span></code>。</p>
|
||
<p>你也可以为你的模块定义一个唯一的新异常。需要在文件前部声明一个静态对象变量,如:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">SpamError</span><span class="p">;</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>并且在你的模块的初始化函数 (<code class="xref c c-func docutils literal notranslate"><span class="pre">PyInit_spam()</span></code>) 中使用一个异常对象来初始化:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">PyMODINIT_FUNC</span>
|
||
<span class="nf">PyInit_spam</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">m</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyModule_Create</span><span class="p">(</span><span class="o">&</span><span class="n">spammodule</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="n">SpamError</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyErr_NewException</span><span class="p">(</span><span class="s">"spam.error"</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">Py_XINCREF</span><span class="p">(</span><span class="n">SpamError</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">PyModule_AddObject</span><span class="p">(</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="s">"error"</span><span class="p">,</span><span class="w"> </span><span class="n">SpamError</span><span class="p">)</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">Py_XDECREF</span><span class="p">(</span><span class="n">SpamError</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">Py_CLEAR</span><span class="p">(</span><span class="n">SpamError</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">Py_DECREF</span><span class="p">(</span><span class="n">m</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">m</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>注意异常对象的Python名字是 <code class="xref py py-exc docutils literal notranslate"><span class="pre">spam.error</span></code> 。而 <a class="reference internal" href="../c-api/exceptions.html#c.PyErr_NewException" title="PyErr_NewException"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_NewException()</span></code></a> 函数可以创建一个类,其基类为 <a class="reference internal" href="../library/exceptions.html#Exception" title="Exception"><code class="xref py py-exc docutils literal notranslate"><span class="pre">Exception</span></code></a> (除非是另一个类传入以替换 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> ), 细节参见 <a class="reference internal" href="../library/exceptions.html#bltin-exceptions"><span class="std std-ref">内置异常</span></a> 。</p>
|
||
<p>同样注意的是创建类保存了 <code class="xref c c-data docutils literal notranslate"><span class="pre">SpamError</span></code> 的一个引用,这是有意的。为了防止被垃圾回收掉,否则 <code class="xref c c-data docutils literal notranslate"><span class="pre">SpamError</span></code> 随时会成为野指针。</p>
|
||
<p>一会讨论 <code class="docutils literal notranslate"><span class="pre">PyMODINIT_FUNC</span></code> 作为函数返回类型的用法。</p>
|
||
<p><code class="xref py py-exc docutils literal notranslate"><span class="pre">spam.error</span></code> 异常可以在扩展模块中抛出,通过 <a class="reference internal" href="../c-api/exceptions.html#c.PyErr_SetString" title="PyErr_SetString"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_SetString()</span></code></a> 函数调用,如下:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span>
|
||
<span class="nf">spam_system</span><span class="p">(</span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">command</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">sts</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="s">"s"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">command</span><span class="p">))</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="n">sts</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">system</span><span class="p">(</span><span class="n">command</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">sts</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PyErr_SetString</span><span class="p">(</span><span class="n">SpamError</span><span class="p">,</span><span class="w"> </span><span class="s">"System command failed"</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">PyLong_FromLong</span><span class="p">(</span><span class="n">sts</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="back-to-the-example">
|
||
<span id="backtoexample"></span><h2><span class="section-number">1.3. </span>回到例子<a class="headerlink" href="#back-to-the-example" title="永久链接至标题">¶</a></h2>
|
||
<p>回到前面的例子,你应该明白下面的代码:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="s">"s"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">command</span><span class="p">))</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>如果在参数列表中检测到错误,它将返回 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> (该值是返回对象指针的函数所使用的错误提示),这取决于 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 设置的异常。 在其他情况下参数的字符串值会被拷贝到局部变量 <code class="xref c c-data docutils literal notranslate"><span class="pre">command</span></code>。 这是一个指针赋值并且你不应该修改它所指向的字符串 (因此在标准 C 中,变量 <code class="xref c c-data docutils literal notranslate"><span class="pre">command</span></code> 应当被正确地声明为 <code class="docutils literal notranslate"><span class="pre">const</span> <span class="pre">char</span> <span class="pre">*command</span></code>)。</p>
|
||
<p>下一个语句使用UNIX系统函数 <code class="xref c c-func docutils literal notranslate"><span class="pre">system()</span></code> ,传递给他的参数是刚才从 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 取出的:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">sts</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">system</span><span class="p">(</span><span class="n">command</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>我们的 <code class="xref py py-func docutils literal notranslate"><span class="pre">spam.system()</span></code> 函数必须返回 <code class="xref c c-data docutils literal notranslate"><span class="pre">sts</span></code> 的值作为Python对象。这通过使用函数 <a class="reference internal" href="../c-api/long.html#c.PyLong_FromLong" title="PyLong_FromLong"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyLong_FromLong()</span></code></a> 来实现。</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">return</span><span class="w"> </span><span class="n">PyLong_FromLong</span><span class="p">(</span><span class="n">sts</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>在这种情况下,会返回一个整数对象,(这个对象会在Python堆里面管理)。</p>
|
||
<p>如果你的 C 函数没有有用的返回值 (返回 <code class="xref c c-type docutils literal notranslate"><span class="pre">void</span></code> 的函数),则对应的 Python 函数必须返回 <code class="docutils literal notranslate"><span class="pre">None</span></code>。 你必须使用这种写法(可以通过 <a class="reference internal" href="../c-api/none.html#c.Py_RETURN_NONE" title="Py_RETURN_NONE"><code class="xref c c-macro docutils literal notranslate"><span class="pre">Py_RETURN_NONE</span></code></a> 宏来实现):</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">Py_INCREF</span><span class="p">(</span><span class="n">Py_None</span><span class="p">);</span>
|
||
<span class="k">return</span><span class="w"> </span><span class="n">Py_None</span><span class="p">;</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><a class="reference internal" href="../c-api/none.html#c.Py_None" title="Py_None"><code class="xref c c-data docutils literal notranslate"><span class="pre">Py_None</span></code></a> 是特殊 Python 对象 <code class="docutils literal notranslate"><span class="pre">None</span></code> 所对应的 C 名称。 它是一个真正的 Python 对象而不是 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 指针,如我们所见,后者在大多数上下文中都意味着“错误”。</p>
|
||
</section>
|
||
<section id="the-module-s-method-table-and-initialization-function">
|
||
<span id="methodtable"></span><h2><span class="section-number">1.4. </span>模块方法表和初始化函数<a class="headerlink" href="#the-module-s-method-table-and-initialization-function" title="永久链接至标题">¶</a></h2>
|
||
<p>为了展示 <code class="xref c c-func docutils literal notranslate"><span class="pre">spam_system()</span></code> 如何被Python程序调用。把函数声明为可以被Python调用,需要先定义一个方法表 "method table" 。</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="n">PyMethodDef</span><span class="w"> </span><span class="n">SpamMethods</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="p">...</span>
|
||
<span class="w"> </span><span class="p">{</span><span class="s">"system"</span><span class="p">,</span><span class="w"> </span><span class="n">spam_system</span><span class="p">,</span><span class="w"> </span><span class="n">METH_VARARGS</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s">"Execute a shell command."</span><span class="p">},</span>
|
||
<span class="w"> </span><span class="p">...</span>
|
||
<span class="w"> </span><span class="p">{</span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">}</span><span class="w"> </span><span class="cm">/* Sentinel */</span>
|
||
<span class="p">};</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>注意第三个参数 ( <code class="docutils literal notranslate"><span class="pre">METH_VARARGS</span></code> ) ,这个标志指定会使用C的调用惯例。可选值有 <code class="docutils literal notranslate"><span class="pre">METH_VARARGS</span></code> 、 <code class="docutils literal notranslate"><span class="pre">METH_VARARGS</span> <span class="pre">|</span> <span class="pre">METH_KEYWORDS</span></code> 。值 <code class="docutils literal notranslate"><span class="pre">0</span></code> 代表使用 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 的陈旧变量。</p>
|
||
<p>如果单独使用 <code class="docutils literal notranslate"><span class="pre">METH_VARARGS</span></code> ,函数会等待Python传来tuple格式的参数,并最终使用 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 进行解析。</p>
|
||
<p><code class="xref py py-const docutils literal notranslate"><span class="pre">METH_KEYWORDS</span></code> 值表示接受关键字参数。这种情况下C函数需要接受第三个 <code class="docutils literal notranslate"><span class="pre">PyObject</span> <span class="pre">*</span></code> 对象,表示字典参数,使用 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTupleAndKeywords" title="PyArg_ParseTupleAndKeywords"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTupleAndKeywords()</span></code></a> 来解析出参数。</p>
|
||
<p>这个方法表必须被模块定义结构所引用。</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">PyModuleDef</span><span class="w"> </span><span class="n">spammodule</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PyModuleDef_HEAD_INIT</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s">"spam"</span><span class="p">,</span><span class="w"> </span><span class="cm">/* name of module */</span>
|
||
<span class="w"> </span><span class="n">spam_doc</span><span class="p">,</span><span class="w"> </span><span class="cm">/* module documentation, may be NULL */</span>
|
||
<span class="w"> </span><span class="mi">-1</span><span class="p">,</span><span class="w"> </span><span class="cm">/* size of per-interpreter state of the module,</span>
|
||
<span class="cm"> or -1 if the module keeps state in global variables. */</span>
|
||
<span class="w"> </span><span class="n">SpamMethods</span>
|
||
<span class="p">};</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这个结构体必须传递给解释器的模块初始化函数。初始化函数必须命名为 <code class="xref c c-func docutils literal notranslate"><span class="pre">PyInit_name()</span></code> ,其中 <em>name</em> 是模块的名字,并应该定义为非 <code class="docutils literal notranslate"><span class="pre">static</span></code> ,且在模块文件里:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">PyMODINIT_FUNC</span>
|
||
<span class="nf">PyInit_spam</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">PyModule_Create</span><span class="p">(</span><span class="o">&</span><span class="n">spammodule</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>注意 PyMODINIT_FUNC 将函数声明为 <code class="docutils literal notranslate"><span class="pre">PyObject</span> <span class="pre">*</span></code> 返回类型,声明了任何平台所要求的特殊链接声明,并针对 C++ 将函数声明为 <code class="docutils literal notranslate"><span class="pre">extern</span> <span class="pre">"C"</span></code>。</p>
|
||
<p>当 Python 程序首次导入 <code class="xref py py-mod docutils literal notranslate"><span class="pre">spam</span></code> 模块时, <code class="xref c c-func docutils literal notranslate"><span class="pre">PyInit_spam()</span></code> 会被调用。 (有关嵌入 Python 的注释参见下文。) 它将调用 <a class="reference internal" href="../c-api/module.html#c.PyModule_Create" title="PyModule_Create"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyModule_Create()</span></code></a>,该函数会返回一个模块对象,并基于在模块定义中找到的表将内置函数对象插入到新创建的模块中(该表是一个 <a class="reference internal" href="../c-api/structures.html#c.PyMethodDef" title="PyMethodDef"><code class="xref c c-type docutils literal notranslate"><span class="pre">PyMethodDef</span></code></a> 结构体的数组)。 <a class="reference internal" href="../c-api/module.html#c.PyModule_Create" title="PyModule_Create"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyModule_Create()</span></code></a> 返回一个指向它所创建的模块对象的指针。 它可能会因程度严重的特定错误而中止,或者在模块无法成功初始化时返回 <code class="docutils literal notranslate"><span class="pre">NULL</span></code>。 初始化函数必须返回模块对象给其调用者,这样它就可以被插入到 <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code> 中。</p>
|
||
<p>当嵌入Python时, <code class="xref c c-func docutils literal notranslate"><span class="pre">PyInit_spam()</span></code> 函数不会被自动调用,除非放在 <code class="xref c c-data docutils literal notranslate"><span class="pre">PyImport_Inittab</span></code> 表里。要添加模块到初始化表,使用 <a class="reference internal" href="../c-api/import.html#c.PyImport_AppendInittab" title="PyImport_AppendInittab"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyImport_AppendInittab()</span></code></a> ,可选的跟着一个模块的导入。</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="kt">int</span>
|
||
<span class="nf">main</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">argc</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">argv</span><span class="p">[])</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="kt">wchar_t</span><span class="w"> </span><span class="o">*</span><span class="n">program</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Py_DecodeLocale</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="w"> </span><span class="nb">NULL</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">program</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="w"> </span><span class="s">"Fatal error: cannot decode argv[0]</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
|
||
<span class="w"> </span><span class="cm">/* Add a built-in module, before Py_Initialize */</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">PyImport_AppendInittab</span><span class="p">(</span><span class="s">"spam"</span><span class="p">,</span><span class="w"> </span><span class="n">PyInit_spam</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">-1</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="w"> </span><span class="s">"Error: could not extend in-built modules table</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
|
||
<span class="w"> </span><span class="cm">/* Pass argv[0] to the Python interpreter */</span>
|
||
<span class="w"> </span><span class="n">Py_SetProgramName</span><span class="p">(</span><span class="n">program</span><span class="p">);</span>
|
||
|
||
<span class="w"> </span><span class="cm">/* Initialize the Python interpreter. Required.</span>
|
||
<span class="cm"> If this step fails, it will be a fatal error. */</span>
|
||
<span class="w"> </span><span class="n">Py_Initialize</span><span class="p">();</span>
|
||
|
||
<span class="w"> </span><span class="cm">/* Optionally import the module; alternatively,</span>
|
||
<span class="cm"> import can be deferred until the embedded script</span>
|
||
<span class="cm"> imports it. */</span>
|
||
<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">pmodule</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyImport_ImportModule</span><span class="p">(</span><span class="s">"spam"</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">pmodule</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PyErr_Print</span><span class="p">();</span>
|
||
<span class="w"> </span><span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span><span class="w"> </span><span class="s">"Error: could not import module 'spam'</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
|
||
<span class="w"> </span><span class="p">...</span>
|
||
|
||
<span class="w"> </span><span class="n">PyMem_RawFree</span><span class="p">(</span><span class="n">program</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="admonition note">
|
||
<p class="admonition-title">注解</p>
|
||
<p>要从 <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code> 删除实体或导入已编译模块到一个进程里的多个解释器(或使用 <code class="xref c c-func docutils literal notranslate"><span class="pre">fork()</span></code> 而没用 <code class="xref c c-func docutils literal notranslate"><span class="pre">exec()</span></code> )会在一些扩展模块上产生错误。扩展模块作者可以在初始化内部数据结构时给出警告。</p>
|
||
</div>
|
||
<p>更多关于模块的现实的例子包含在 Python 源码发布包的 <code class="file docutils literal notranslate"><span class="pre">Modules/xxmodule.c</span></code> 中。 此文件可以被用作代码模板或是学习样例。</p>
|
||
<div class="admonition note">
|
||
<p class="admonition-title">注解</p>
|
||
<p>不像我们的 <code class="docutils literal notranslate"><span class="pre">spam</span></code> 例子, <code class="docutils literal notranslate"><span class="pre">xxmodule</span></code> 使用了 <em>多阶段初始化</em> (Python3.5开始引入), <code class="docutils literal notranslate"><span class="pre">PyInit_spam</span></code> 会返回一个 PyModuleDef 结构体,然后创建的模块放到导入机制。细节参考 <span class="target" id="index-6"></span><a class="pep reference external" href="https://www.python.org/dev/peps/pep-0489"><strong>PEP 489</strong></a> 的多阶段初始化。</p>
|
||
</div>
|
||
</section>
|
||
<section id="compilation-and-linkage">
|
||
<span id="compilation"></span><h2><span class="section-number">1.5. </span>编译和链接<a class="headerlink" href="#compilation-and-linkage" title="永久链接至标题">¶</a></h2>
|
||
<p>在你能使用你的新写的扩展之前,你还需要做两件事情:使用 Python 系统来编译和链接。如果你使用动态加载,这取决于你使用的操作系统的动态加载机制;更多信息请参考编译扩展模块的章节( <a class="reference internal" href="building.html#building"><span class="std std-ref">构建C/C++扩展</span></a> 章节),以及在 Windows 上编译需要的额外信息( <a class="reference internal" href="windows.html#building-on-windows"><span class="std std-ref">在Windows平台编译C和C++扩展</span></a> 章节)。</p>
|
||
<p>如果你不使用动态加载,或者想要让模块永久性的作为Python解释器的一部分,就必须修改配置设置,并重新构建解释器。幸运的是在Unix上很简单,只需要把你的文件 ( <code class="file docutils literal notranslate"><span class="pre">spammodule.c</span></code> 为例) 放在解压缩源码发行包的 <code class="file docutils literal notranslate"><span class="pre">Modules/</span></code> 目录下,添加一行到 <code class="file docutils literal notranslate"><span class="pre">Modules/Setup.local</span></code> 来描述你的文件:</p>
|
||
<div class="highlight-sh notranslate"><div class="highlight"><pre><span></span>spam<span class="w"> </span>spammodule.o
|
||
</pre></div>
|
||
</div>
|
||
<p>然后在顶层目录运行 <strong class="program">make</strong> 来重新构建解释器。你也可以在 <code class="file docutils literal notranslate"><span class="pre">Modules/</span></code> 子目录使用 <strong class="program">make</strong>,但是你必须先重建 <code class="file docutils literal notranslate"><span class="pre">Makefile</span></code> 文件,然后运行 '<strong class="program">make</strong> Makefile' 命令。(你每次修改 <code class="file docutils literal notranslate"><span class="pre">Setup</span></code> 文件都需要这样操作。)</p>
|
||
<p>如果你的模块需要额外的链接,这些内容可以列出在配置文件里,举个实例:</p>
|
||
<div class="highlight-sh notranslate"><div class="highlight"><pre><span></span>spam<span class="w"> </span>spammodule.o<span class="w"> </span>-lX11
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="calling-python-functions-from-c">
|
||
<span id="callingpython"></span><h2><span class="section-number">1.6. </span>在C中调用Python函数<a class="headerlink" href="#calling-python-functions-from-c" title="永久链接至标题">¶</a></h2>
|
||
<p>迄今为止,我们一直把注意力集中于让Python调用C函数,其实反过来也很有用,就是用C调用Python函数。这在回调函数中尤其有用。如果一个C接口使用回调,那么就要实现这个回调机制。</p>
|
||
<p>幸运的是,Python解释器是比较方便回调的,并给标准Python函数提供了标准接口。(这里就不再详述解析Python字符串作为输入的方式,如果有兴趣可以参考 <code class="file docutils literal notranslate"><span class="pre">Python/pythonmain.c</span></code> 中的 <a class="reference internal" href="../using/cmdline.html#cmdoption-c"><code class="xref std std-option docutils literal notranslate"><span class="pre">-c</span></code></a> 命令行代码。)</p>
|
||
<p>调用Python函数很简单,首先Python程序要传递Python函数对象。应该提供个函数(或其他接口)来实现。当调用这个函数时,用全局变量保存Python函数对象的指针,还要调用 (<a class="reference internal" href="../c-api/refcounting.html#c.Py_INCREF" title="Py_INCREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_INCREF()</span></code></a>) 来增加引用计数,当然不用全局变量也没什么关系。举个例子,如下函数可能是模块定义的一部分:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">my_callback</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
|
||
<span class="k">static</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span>
|
||
<span class="nf">my_set_callback</span><span class="p">(</span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">dummy</span><span class="p">,</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">temp</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="s">"O:set_callback"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">temp</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">PyCallable_Check</span><span class="p">(</span><span class="n">temp</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PyErr_SetString</span><span class="p">(</span><span class="n">PyExc_TypeError</span><span class="p">,</span><span class="w"> </span><span class="s">"parameter must be callable"</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="n">Py_XINCREF</span><span class="p">(</span><span class="n">temp</span><span class="p">);</span><span class="w"> </span><span class="cm">/* Add a reference to new callback */</span>
|
||
<span class="w"> </span><span class="n">Py_XDECREF</span><span class="p">(</span><span class="n">my_callback</span><span class="p">);</span><span class="w"> </span><span class="cm">/* Dispose of previous callback */</span>
|
||
<span class="w"> </span><span class="n">my_callback</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">temp</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Remember new callback */</span>
|
||
<span class="w"> </span><span class="cm">/* Boilerplate to return "None" */</span>
|
||
<span class="w"> </span><span class="n">Py_INCREF</span><span class="p">(</span><span class="n">Py_None</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Py_None</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">result</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这个函数必须使用 <a class="reference internal" href="../c-api/structures.html#METH_VARARGS" title="METH_VARARGS"><code class="xref py py-const docutils literal notranslate"><span class="pre">METH_VARARGS</span></code></a> 标志注册到解释器,这在 <a class="reference internal" href="#methodtable"><span class="std std-ref">模块方法表和初始化函数</span></a> 章节会描述。 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 函数及其参数的文档在 <a class="reference internal" href="#parsetuple"><span class="std std-ref">提取扩展函数的参数</span></a> 。</p>
|
||
<p><a class="reference internal" href="../c-api/refcounting.html#c.Py_XINCREF" title="Py_XINCREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_XINCREF()</span></code></a> 和 <a class="reference internal" href="../c-api/refcounting.html#c.Py_XDECREF" title="Py_XDECREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_XDECREF()</span></code></a> 这两个宏可增加/减少一个对象的引用计数,并且当存在 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 指针时仍可保证安全 (但请注意在这个上下文中 <em>temp</em> 将不为 <code class="docutils literal notranslate"><span class="pre">NULL</span></code>)。 更多相关信息请参考 <a class="reference internal" href="#refcounts"><span class="std std-ref">引用计数</span></a> 章节。</p>
|
||
<p id="index-1">随后,当要调用此函数时,你将调用 C 函数 <a class="reference internal" href="../c-api/object.html#c.PyObject_CallObject" title="PyObject_CallObject"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyObject_CallObject()</span></code></a>。 该函数有两个参数,它们都属于指针,指向任意 Python 对象:即 Python 函数,及其参数列表。 参数列表必须总是一个元组对象,其长度即参数的个数量。 要不带参数地调用 Python 函数,则传入 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 或一个空元组;要带一个参数调用它,则传入一个单元组。 <a class="reference internal" href="../c-api/arg.html#c.Py_BuildValue" title="Py_BuildValue"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_BuildValue()</span></code></a> 会在其格式字符串包含一对圆括号内的零个或多个格式代码时返回一个元组。 例如:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="kt">int</span><span class="w"> </span><span class="n">arg</span><span class="p">;</span>
|
||
<span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">arglist</span><span class="p">;</span>
|
||
<span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">result</span><span class="p">;</span>
|
||
<span class="p">...</span>
|
||
<span class="n">arg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">123</span><span class="p">;</span>
|
||
<span class="p">...</span>
|
||
<span class="cm">/* Time to call the callback */</span>
|
||
<span class="n">arglist</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Py_BuildValue</span><span class="p">(</span><span class="s">"(i)"</span><span class="p">,</span><span class="w"> </span><span class="n">arg</span><span class="p">);</span>
|
||
<span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyObject_CallObject</span><span class="p">(</span><span class="n">my_callback</span><span class="p">,</span><span class="w"> </span><span class="n">arglist</span><span class="p">);</span>
|
||
<span class="n">Py_DECREF</span><span class="p">(</span><span class="n">arglist</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><a class="reference internal" href="../c-api/object.html#c.PyObject_CallObject" title="PyObject_CallObject"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyObject_CallObject()</span></code></a> 返回Python对象指针,这也是Python函数的返回值。 <a class="reference internal" href="../c-api/object.html#c.PyObject_CallObject" title="PyObject_CallObject"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyObject_CallObject()</span></code></a> 是一个对其参数 "引用计数无关" 的函数。例子中新的元组创建用于参数列表,并且在 <a class="reference internal" href="../c-api/object.html#c.PyObject_CallObject" title="PyObject_CallObject"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyObject_CallObject()</span></code></a> 之后立即使用了 <a class="reference internal" href="../c-api/refcounting.html#c.Py_DECREF" title="Py_DECREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_DECREF()</span></code></a> 。</p>
|
||
<p><a class="reference internal" href="../c-api/object.html#c.PyObject_CallObject" title="PyObject_CallObject"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyObject_CallObject()</span></code></a> 的返回值总是“新”的:要么是一个新建的对象;要么是已有对象,但增加了引用计数。所以除非你想把结果保存在全局变量中,你需要对这个值使用 <a class="reference internal" href="../c-api/refcounting.html#c.Py_DECREF" title="Py_DECREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_DECREF()</span></code></a>,即使你对里面的内容(特别!)不感兴趣。</p>
|
||
<p>但是在你这么做之前,很重要的一点是检查返回值不是 <code class="docutils literal notranslate"><span class="pre">NULL</span></code>。 如果是的话,Python 函数会终止并引发异常。 如果调用 <a class="reference internal" href="../c-api/object.html#c.PyObject_CallObject" title="PyObject_CallObject"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyObject_CallObject()</span></code></a> 的 C 代码是在 Python 中发起调用的,它应当立即返回一个错误来告知其 Python 调用者,以便解释器能打印栈回溯信息,或者让调用方 Python 代码能处理该异常。 如果这无法做到或不合本意,则应当通过调用 <a class="reference internal" href="../c-api/exceptions.html#c.PyErr_Clear" title="PyErr_Clear"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyErr_Clear()</span></code></a> 来清除异常。 例如:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">result</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Pass error back */</span>
|
||
<span class="p">...</span><span class="n">use</span><span class="w"> </span><span class="n">result</span><span class="p">...</span>
|
||
<span class="n">Py_DECREF</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>依赖于具体的回调函数,你还要提供一个参数列表到 <a class="reference internal" href="../c-api/object.html#c.PyObject_CallObject" title="PyObject_CallObject"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyObject_CallObject()</span></code></a> 。在某些情况下参数列表是由Python程序提供的,通过接口再传到回调函数对象。这样就可以不改变形式直接传递。另外一些时候你要构造一个新的元组来传递参数。最简单的方法就是 <a class="reference internal" href="../c-api/arg.html#c.Py_BuildValue" title="Py_BuildValue"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_BuildValue()</span></code></a> 函数构造tuple。举个例子,你要传递一个事件代码时可以用如下代码:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">arglist</span><span class="p">;</span>
|
||
<span class="p">...</span>
|
||
<span class="n">arglist</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Py_BuildValue</span><span class="p">(</span><span class="s">"(l)"</span><span class="p">,</span><span class="w"> </span><span class="n">eventcode</span><span class="p">);</span>
|
||
<span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyObject_CallObject</span><span class="p">(</span><span class="n">my_callback</span><span class="p">,</span><span class="w"> </span><span class="n">arglist</span><span class="p">);</span>
|
||
<span class="n">Py_DECREF</span><span class="p">(</span><span class="n">arglist</span><span class="p">);</span>
|
||
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">result</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Pass error back */</span>
|
||
<span class="cm">/* Here maybe use the result */</span>
|
||
<span class="n">Py_DECREF</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>注意 <code class="docutils literal notranslate"><span class="pre">Py_DECREF(arglist)</span></code> 所在处会立即调用,在错误检查之前。当然还要注意一些常规的错误,比如 <a class="reference internal" href="../c-api/arg.html#c.Py_BuildValue" title="Py_BuildValue"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_BuildValue()</span></code></a> 可能会遭遇内存不足等等。</p>
|
||
<p>当你调用函数时还需要注意,用关键字参数调用 <a class="reference internal" href="../c-api/object.html#c.PyObject_Call" title="PyObject_Call"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyObject_Call()</span></code></a> ,需要支持普通参数和关键字参数。有如如上例子中,我们使用 <a class="reference internal" href="../c-api/arg.html#c.Py_BuildValue" title="Py_BuildValue"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_BuildValue()</span></code></a> 来构造字典。</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">dict</span><span class="p">;</span>
|
||
<span class="p">...</span>
|
||
<span class="n">dict</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Py_BuildValue</span><span class="p">(</span><span class="s">"{s:i}"</span><span class="p">,</span><span class="w"> </span><span class="s">"name"</span><span class="p">,</span><span class="w"> </span><span class="n">val</span><span class="p">);</span>
|
||
<span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyObject_Call</span><span class="p">(</span><span class="n">my_callback</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="n">dict</span><span class="p">);</span>
|
||
<span class="n">Py_DECREF</span><span class="p">(</span><span class="n">dict</span><span class="p">);</span>
|
||
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">result</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span><span class="w"> </span><span class="cm">/* Pass error back */</span>
|
||
<span class="cm">/* Here maybe use the result */</span>
|
||
<span class="n">Py_DECREF</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="extracting-parameters-in-extension-functions">
|
||
<span id="parsetuple"></span><h2><span class="section-number">1.7. </span>提取扩展函数的参数<a class="headerlink" href="#extracting-parameters-in-extension-functions" title="永久链接至标题">¶</a></h2>
|
||
<p id="index-2">函数 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 的声明如下:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="kt">int</span><span class="w"> </span><span class="nf">PyArg_ParseTuple</span><span class="p">(</span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">arg</span><span class="p">,</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">format</span><span class="p">,</span><span class="w"> </span><span class="p">...);</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>参数 <em>arg</em> 必须是一个元组对象,包含从 Python 传递给 C 函数的参数列表。<em>format</em> 参数必须是一个格式字符串,语法请参考 Python C/API 手册中的 <a class="reference internal" href="../c-api/arg.html#arg-parsing"><span class="std std-ref">解析参数并构建值变量</span></a>。剩余参数是各个变量的地址,类型要与格式字符串对应。</p>
|
||
<p>注意 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 会检测他需要的Python参数类型,却无法检测传递给他的C变量地址,如果这里出错了,可能会在内存中随机写入东西,小心。</p>
|
||
<p>注意任何由调用者提供的 Python 对象引用是 <em>借来的</em> 引用;不要递减它们的引用计数!</p>
|
||
<p>一些调用的例子:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cp">#define PY_SSIZE_T_CLEAN </span><span class="cm">/* Make "s#" use Py_ssize_t rather than int. */</span>
|
||
<span class="cp">#include</span><span class="w"> </span><span class="cpf"><Python.h></span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="kt">int</span><span class="w"> </span><span class="n">ok</span><span class="p">;</span>
|
||
<span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="n">j</span><span class="p">;</span>
|
||
<span class="kt">long</span><span class="w"> </span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">l</span><span class="p">;</span>
|
||
<span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">s</span><span class="p">;</span>
|
||
<span class="n">Py_ssize_t</span><span class="w"> </span><span class="n">size</span><span class="p">;</span>
|
||
|
||
<span class="n">ok</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">);</span><span class="w"> </span><span class="cm">/* No arguments */</span>
|
||
<span class="w"> </span><span class="cm">/* Python call: f() */</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">ok</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="s">"s"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">s</span><span class="p">);</span><span class="w"> </span><span class="cm">/* A string */</span>
|
||
<span class="w"> </span><span class="cm">/* Possible Python call: f('whoops!') */</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">ok</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="s">"lls"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">l</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">s</span><span class="p">);</span><span class="w"> </span><span class="cm">/* Two longs and a string */</span>
|
||
<span class="w"> </span><span class="cm">/* Possible Python call: f(1, 2, 'three') */</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">ok</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="s">"(ii)s#"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">j</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">size</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="cm">/* A pair of ints and a string, whose size is also returned */</span>
|
||
<span class="w"> </span><span class="cm">/* Possible Python call: f((1, 2), 'three') */</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
|
||
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">file</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">mode</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"r"</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">bufsize</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="n">ok</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="s">"s|si"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">file</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">mode</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">bufsize</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="cm">/* A string, and optionally another string and an integer */</span>
|
||
<span class="w"> </span><span class="cm">/* Possible Python calls:</span>
|
||
<span class="cm"> f('spam')</span>
|
||
<span class="cm"> f('spam', 'w')</span>
|
||
<span class="cm"> f('spam', 'wb', 100000) */</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
|
||
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">left</span><span class="p">,</span><span class="w"> </span><span class="n">top</span><span class="p">,</span><span class="w"> </span><span class="n">right</span><span class="p">,</span><span class="w"> </span><span class="n">bottom</span><span class="p">,</span><span class="w"> </span><span class="n">h</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="n">ok</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="s">"((ii)(ii))(ii)"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="o">&</span><span class="n">left</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">top</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">right</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">bottom</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">h</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">v</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="cm">/* A rectangle and a point */</span>
|
||
<span class="w"> </span><span class="cm">/* Possible Python call:</span>
|
||
<span class="cm"> f(((0, 0), (400, 300)), (10, 10)) */</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">Py_complex</span><span class="w"> </span><span class="n">c</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="n">ok</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="s">"D:myfunction"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">c</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="cm">/* a complex, also providing a function name for errors */</span>
|
||
<span class="w"> </span><span class="cm">/* Possible Python call: myfunction(1+2j) */</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="keyword-parameters-for-extension-functions">
|
||
<span id="parsetupleandkeywords"></span><h2><span class="section-number">1.8. </span>给扩展函数的关键字参数<a class="headerlink" href="#keyword-parameters-for-extension-functions" title="永久链接至标题">¶</a></h2>
|
||
<p id="index-3">函数 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTupleAndKeywords" title="PyArg_ParseTupleAndKeywords"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTupleAndKeywords()</span></code></a> 声明如下:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="kt">int</span><span class="w"> </span><span class="nf">PyArg_ParseTupleAndKeywords</span><span class="p">(</span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">arg</span><span class="p">,</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">kwdict</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">format</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">kwlist</span><span class="p">[],</span><span class="w"> </span><span class="p">...);</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><em>arg</em> 与 <em>format</em> 形参与 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 函数所定义的一致。 <em>kwdict</em> 形参是作为第三个参数从 Python 运行时接收的关键字字典。 <em>kwlist</em> 形参是以 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 结尾的字符串列表,它被用来标识形参;名称从左至右与来自 <em>format</em> 的类型信息相匹配。 如果执行成功,<a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTupleAndKeywords" title="PyArg_ParseTupleAndKeywords"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTupleAndKeywords()</span></code></a> 会返回真值,否则返回假值并引发一个适当的异常。</p>
|
||
<div class="admonition note">
|
||
<p class="admonition-title">注解</p>
|
||
<p>嵌套的元组在使用关键字参数时无法生效,不在 <em>kwlist</em> 中的关键字参数会导致 <a class="reference internal" href="../library/exceptions.html#TypeError" title="TypeError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">TypeError</span></code></a> 异常。</p>
|
||
</div>
|
||
<p id="index-4">如下例子是使用关键字参数的例子模块,作者是 Geoff Philbrick (<a class="reference external" href="mailto:philbrick%40hks.com">philbrick<span>@</span>hks<span>.</span>com</a>):</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cp">#define PY_SSIZE_T_CLEAN </span><span class="cm">/* Make "s#" use Py_ssize_t rather than int. */</span>
|
||
<span class="cp">#include</span><span class="w"> </span><span class="cpf"><Python.h></span>
|
||
|
||
<span class="k">static</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span>
|
||
<span class="nf">keywdarg_parrot</span><span class="p">(</span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">keywds</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">voltage</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">state</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"a stiff"</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">action</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"voom"</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"Norwegian Blue"</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="k">static</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">kwlist</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="s">"voltage"</span><span class="p">,</span><span class="w"> </span><span class="s">"state"</span><span class="p">,</span><span class="w"> </span><span class="s">"action"</span><span class="p">,</span><span class="w"> </span><span class="s">"type"</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">};</span>
|
||
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">PyArg_ParseTupleAndKeywords</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="n">keywds</span><span class="p">,</span><span class="w"> </span><span class="s">"i|sss"</span><span class="p">,</span><span class="w"> </span><span class="n">kwlist</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="o">&</span><span class="n">voltage</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">state</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">action</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">type</span><span class="p">))</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"-- This parrot wouldn't %s if you put %i Volts through it.</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">action</span><span class="p">,</span><span class="w"> </span><span class="n">voltage</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">"-- Lovely plumage, the %s -- It's %s!</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="p">,</span><span class="w"> </span><span class="n">state</span><span class="p">);</span>
|
||
|
||
<span class="w"> </span><span class="n">Py_RETURN_NONE</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="k">static</span><span class="w"> </span><span class="n">PyMethodDef</span><span class="w"> </span><span class="n">keywdarg_methods</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="cm">/* The cast of the function is necessary since PyCFunction values</span>
|
||
<span class="cm"> * only take two PyObject* parameters, and keywdarg_parrot() takes</span>
|
||
<span class="cm"> * three.</span>
|
||
<span class="cm"> */</span>
|
||
<span class="w"> </span><span class="p">{</span><span class="s">"parrot"</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="n">PyCFunction</span><span class="p">)(</span><span class="kt">void</span><span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="kt">void</span><span class="p">))</span><span class="n">keywdarg_parrot</span><span class="p">,</span><span class="w"> </span><span class="n">METH_VARARGS</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">METH_KEYWORDS</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s">"Print a lovely skit to standard output."</span><span class="p">},</span>
|
||
<span class="w"> </span><span class="p">{</span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">}</span><span class="w"> </span><span class="cm">/* sentinel */</span>
|
||
<span class="p">};</span>
|
||
|
||
<span class="k">static</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">PyModuleDef</span><span class="w"> </span><span class="n">keywdargmodule</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PyModuleDef_HEAD_INIT</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="s">"keywdarg"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nb">NULL</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="mi">-1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="n">keywdarg_methods</span>
|
||
<span class="p">};</span>
|
||
|
||
<span class="n">PyMODINIT_FUNC</span>
|
||
<span class="nf">PyInit_keywdarg</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">PyModule_Create</span><span class="p">(</span><span class="o">&</span><span class="n">keywdargmodule</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="building-arbitrary-values">
|
||
<span id="buildvalue"></span><h2><span class="section-number">1.9. </span>构造任意值<a class="headerlink" href="#building-arbitrary-values" title="永久链接至标题">¶</a></h2>
|
||
<p>这个函数与 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 很相似,声明如下:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="nf">Py_BuildValue</span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">format</span><span class="p">,</span><span class="w"> </span><span class="p">...);</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>接受一个格式字符串,与 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 相同,但是参数必须是原变量的地址指针(输入给函数,而非输出)。最终返回一个Python对象适合于返回C函数调用给Python代码。</p>
|
||
<p>一个与 <a class="reference internal" href="../c-api/arg.html#c.PyArg_ParseTuple" title="PyArg_ParseTuple"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyArg_ParseTuple()</span></code></a> 的不同是,后面可能需要的要求返回一个元组(Python参数里诶包总是在内部描述为元组),比如用于传递给其他Python函数以参数。 <a class="reference internal" href="../c-api/arg.html#c.Py_BuildValue" title="Py_BuildValue"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_BuildValue()</span></code></a> 并不总是生成元组,在多于1个格式字符串时会生成元组,而如果格式字符串为空则返回 <code class="docutils literal notranslate"><span class="pre">None</span></code> ,一个参数则直接返回该参数的对象。如果要求强制生成一个长度为0的元组,或包含一个元素的元组,需要在格式字符串中加上括号。</p>
|
||
<p>例子(左侧是调用,右侧是Python值结果):</p>
|
||
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Py_BuildValue("") None
|
||
Py_BuildValue("i", 123) 123
|
||
Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)
|
||
Py_BuildValue("s", "hello") 'hello'
|
||
Py_BuildValue("y", "hello") b'hello'
|
||
Py_BuildValue("ss", "hello", "world") ('hello', 'world')
|
||
Py_BuildValue("s#", "hello", 4) 'hell'
|
||
Py_BuildValue("y#", "hello", 4) b'hell'
|
||
Py_BuildValue("()") ()
|
||
Py_BuildValue("(i)", 123) (123,)
|
||
Py_BuildValue("(ii)", 123, 456) (123, 456)
|
||
Py_BuildValue("(i,i)", 123, 456) (123, 456)
|
||
Py_BuildValue("[i,i]", 123, 456) [123, 456]
|
||
Py_BuildValue("{s:i,s:i}",
|
||
"abc", 123, "def", 456) {'abc': 123, 'def': 456}
|
||
Py_BuildValue("((ii)(ii)) (ii)",
|
||
1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="reference-counts">
|
||
<span id="refcounts"></span><h2><span class="section-number">1.10. </span>引用计数<a class="headerlink" href="#reference-counts" title="永久链接至标题">¶</a></h2>
|
||
<p>在C/C++语言中,程序员负责动态分配和回收堆heap当中的内存。在C里,通过函数 <code class="xref c c-func docutils literal notranslate"><span class="pre">malloc()</span></code> 和 <code class="xref c c-func docutils literal notranslate"><span class="pre">free()</span></code> 来完成。在C++里是操作 <code class="docutils literal notranslate"><span class="pre">new</span></code> 和 <code class="docutils literal notranslate"><span class="pre">delete</span></code> 来实现相同的功能。</p>
|
||
<p>每个由 <code class="xref c c-func docutils literal notranslate"><span class="pre">malloc()</span></code> 分配的内存块,最终都要由 <code class="xref c c-func docutils literal notranslate"><span class="pre">free()</span></code> 退回到可用内存池里面去。而调用 <code class="xref c c-func docutils literal notranslate"><span class="pre">free()</span></code> 的时机非常重要,如果一个内存块忘了 <code class="xref c c-func docutils literal notranslate"><span class="pre">free()</span></code> 则会导致内存泄漏,这块内存在程序结束前将无法重新使用。这叫做 <em class="dfn">内存泄漏</em> 。而如果对同一内存块 <code class="xref c c-func docutils literal notranslate"><span class="pre">free()</span></code> 了以后,另外一个指针再次访问,则再次使用 <code class="xref c c-func docutils literal notranslate"><span class="pre">malloc()</span></code> 复用这块内存会导致冲突。这叫做 <em class="dfn">野指针</em> 。等同于使用未初始化的数据,core dump,错误结果,神秘的崩溃等。</p>
|
||
<p>内存泄露往往发生在一些并不常见的代码流程上面。比如一个函数申请了内存以后,做了些计算,然后释放内存块。现在一些对函数的修改可能增加对计算的测试并检测错误条件,然后过早的从函数返回了。这很容易忘记在退出前释放内存,特别是后期修改的代码。这种内存泄漏,一旦引入,通常很长时间都难以检测到,错误退出被调用的频度较低,而现代电脑又有非常巨大的虚拟内存,所以泄漏仅在长期运行或频繁调用泄漏函数时才会变得明显。因此,有必要避免内存泄漏,通过代码规范会策略来最小化此类错误。</p>
|
||
<p>Python通过 <code class="xref c c-func docutils literal notranslate"><span class="pre">malloc()</span></code> 和 <code class="xref c c-func docutils literal notranslate"><span class="pre">free()</span></code> 包含大量的内存分配和释放,同样需要避免内存泄漏和野指针。他选择的方法就是 <em class="dfn">引用计数</em> 。其原理比较简单:每个对象都包含一个计数器,计数器的增减与对象引用的增减直接相关,当引用计数为0时,表示对象已经没有存在的意义了,对象就可以删除了。</p>
|
||
<p>另一个叫法是 <em class="dfn">自动垃圾回收</em> 。(有时引用计数也被看作是垃圾回收策略,于是这里的"自动"用以区分两者)。自动垃圾回收的优点是用户不需要明确的调用 <code class="xref c c-func docutils literal notranslate"><span class="pre">free()</span></code> 。(另一个优点是改善速度或内存使用,然而这并不难)。缺点是对C,没有可移植的自动垃圾回收器,而引用计数则可以可移植的实现(只要 <code class="xref c c-func docutils literal notranslate"><span class="pre">malloc()</span></code> 和 <code class="xref c c-func docutils literal notranslate"><span class="pre">free()</span></code> 函数是可用的,这也是C标准担保的)。也许以后有一天会出现可移植的自动垃圾回收器,但在此前我们必须与引用计数一起工作。</p>
|
||
<p>Python使用传统的引用计数实现,也提供了循环监测器,用以检测引用循环。这使得应用无需担心直接或间接的创建了循环引用,这是引用计数垃圾收集的一个弱点。引用循环是对象(可能直接)的引用了本身,所以循环中的每个对象的引用计数都不是0。典型的引用计数实现无法回收处于引用循环中的对象,或者被循环所引用的对象,哪怕没有循环以外的引用了。</p>
|
||
<p>循环探测器可以检测垃圾循环并回收。 <a class="reference internal" href="../library/gc.html#module-gc" title="gc: Interface to the cycle-detecting garbage collector."><code class="xref py py-mod docutils literal notranslate"><span class="pre">gc</span></code></a> 模块提供了方法运行探测器 ( <a class="reference internal" href="../library/gc.html#gc.collect" title="gc.collect"><code class="xref py py-func docutils literal notranslate"><span class="pre">collect()</span></code></a> 函数) ,而且可以在运行时配置禁用探测器。循环探测器被当作可选组件,默认是包含的,也可以在构建时禁用,在Unix平台(包括Mac OS X)使用 <code class="xref std std-option docutils literal notranslate"><span class="pre">--without-cycle-gc</span></code> 选项到 <strong class="program">configure</strong> 脚本。如果循环探测器被禁用, <a class="reference internal" href="../library/gc.html#module-gc" title="gc: Interface to the cycle-detecting garbage collector."><code class="xref py py-mod docutils literal notranslate"><span class="pre">gc</span></code></a> 模块就不可用了。</p>
|
||
<section id="reference-counting-in-python">
|
||
<span id="refcountsinpython"></span><h3><span class="section-number">1.10.1. </span>Python中的引用计数<a class="headerlink" href="#reference-counting-in-python" title="永久链接至标题">¶</a></h3>
|
||
<p>有两个宏 <code class="docutils literal notranslate"><span class="pre">Py_INCREF(x)</span></code> 和 <code class="docutils literal notranslate"><span class="pre">Py_DECREF(x)</span></code> ,会处理引用计数的增减。 <a class="reference internal" href="../c-api/refcounting.html#c.Py_DECREF" title="Py_DECREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_DECREF()</span></code></a> 也会在引用计数到达0时释放对象。为了灵活,并不会直接调用 <code class="xref c c-func docutils literal notranslate"><span class="pre">free()</span></code> ,而是通过对象的 <em class="dfn">类型对象</em> 的函数指针来调用。为了这个目的(或其他的),每个对象同时包含一个指向自身类型对象的指针。</p>
|
||
<p>最大的问题依旧:何时使用 <code class="docutils literal notranslate"><span class="pre">Py_INCREF(x)</span></code> 和 <code class="docutils literal notranslate"><span class="pre">Py_DECREF(x)</span></code> ?我们首先引入一些概念。没有人"拥有"一个对象,你可以 <em class="dfn">拥有一个引用</em> 到一个对象。一个对象的引用计数定义为拥有引用的数量。引用的拥有者有责任调用 <a class="reference internal" href="../c-api/refcounting.html#c.Py_DECREF" title="Py_DECREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_DECREF()</span></code></a> ,在引用不再需要时。引用的拥有关系可以被传递。有三种办法来处置拥有的引用:传递、存储、调用 <a class="reference internal" href="../c-api/refcounting.html#c.Py_DECREF" title="Py_DECREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_DECREF()</span></code></a> 。忘记处置一个拥有的引用会导致内存泄漏。</p>
|
||
<p>还可以 <em class="dfn">借用</em> <a class="footnote-reference brackets" href="#id6" id="id2">2</a> 一个对象的引用。借用的引用不应该调用 <a class="reference internal" href="../c-api/refcounting.html#c.Py_DECREF" title="Py_DECREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_DECREF()</span></code></a> 。借用者必须确保不能持有对象超过拥有者借出的时间。在拥有者处置对象后使用借用的引用是有风险的,应该完全避免 <a class="footnote-reference brackets" href="#id7" id="id3">3</a> 。</p>
|
||
<p>借用相对于引用的优点是你无需担心整条路径上代码的引用,或者说,通过借用你无需担心内存泄漏的风险。借用的缺点是一些看起来正确代码上的借用可能会在拥有者处置后使用对象。</p>
|
||
<p>借用可以变为拥有引用,通过调用 <a class="reference internal" href="../c-api/refcounting.html#c.Py_INCREF" title="Py_INCREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_INCREF()</span></code></a>。 这不会影响已经借出的拥有者的状态。 这会创建一个新的拥有引用,并给予完全的拥有者责任(新的拥有者必须恰当的处置引用,就像之前的拥有者那样)。</p>
|
||
</section>
|
||
<section id="ownership-rules">
|
||
<span id="ownershiprules"></span><h3><span class="section-number">1.10.2. </span>拥有规则<a class="headerlink" href="#ownership-rules" title="永久链接至标题">¶</a></h3>
|
||
<p>当一个对象引用传递进出一个函数时,函数的接口应该指定拥有关系的传递是否包含引用。</p>
|
||
<p>大多数函数返回一个对象的引用,并传递引用拥有关系。通常,所有创建对象的函数,例如 <a class="reference internal" href="../c-api/long.html#c.PyLong_FromLong" title="PyLong_FromLong"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyLong_FromLong()</span></code></a> 和 <a class="reference internal" href="../c-api/arg.html#c.Py_BuildValue" title="Py_BuildValue"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_BuildValue()</span></code></a> ,会传递拥有关系给接收者。即便是对象不是真正新的,你仍然可以获得对象的新引用。一个实例是 <a class="reference internal" href="../c-api/long.html#c.PyLong_FromLong" title="PyLong_FromLong"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyLong_FromLong()</span></code></a> 维护了一个流行值的缓存,并可以返回已缓存项目的新引用。</p>
|
||
<p>很多另一个对象提取对象的函数,也会传递引用关系,例如 <a class="reference internal" href="../c-api/object.html#c.PyObject_GetAttrString" title="PyObject_GetAttrString"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyObject_GetAttrString()</span></code></a> 。这里的情况不够清晰,一些不太常用的例程是例外的 <a class="reference internal" href="../c-api/tuple.html#c.PyTuple_GetItem" title="PyTuple_GetItem"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyTuple_GetItem()</span></code></a> , <a class="reference internal" href="../c-api/list.html#c.PyList_GetItem" title="PyList_GetItem"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyList_GetItem()</span></code></a> , <a class="reference internal" href="../c-api/dict.html#c.PyDict_GetItem" title="PyDict_GetItem"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyDict_GetItem()</span></code></a> , <a class="reference internal" href="../c-api/dict.html#c.PyDict_GetItemString" title="PyDict_GetItemString"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyDict_GetItemString()</span></code></a> 都是返回从元组、列表、字典里借用的引用。</p>
|
||
<p>函数 <a class="reference internal" href="../c-api/import.html#c.PyImport_AddModule" title="PyImport_AddModule"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyImport_AddModule()</span></code></a> 也会返回借用的引用,哪怕可能会返回创建的对象:这个可能因为一个拥有的引用对象是存储在 <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code> 里。</p>
|
||
<p>当你传递一个对象引用到另一个函数时,通常函数是借用出去的。如果需要存储,就使用 <a class="reference internal" href="../c-api/refcounting.html#c.Py_INCREF" title="Py_INCREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_INCREF()</span></code></a> 来变成独立的拥有者。这个规则有两个重要的例外: <a class="reference internal" href="../c-api/tuple.html#c.PyTuple_SetItem" title="PyTuple_SetItem"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyTuple_SetItem()</span></code></a> 和 <a class="reference internal" href="../c-api/list.html#c.PyList_SetItem" title="PyList_SetItem"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyList_SetItem()</span></code></a> 。这些函数接受传递来的引用关系,哪怕会失败!(注意 <a class="reference internal" href="../c-api/dict.html#c.PyDict_SetItem" title="PyDict_SetItem"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyDict_SetItem()</span></code></a> 及其同类不会接受引用关系,他们是"正常的")。</p>
|
||
<p>当一个C函数被Python调用时,会从调用方传来的参数借用引用。调用者拥有对象的引用,所以借用的引用生命周期可以保证到函数返回。只要当借用的引用需要存储或传递时,就必须转换为拥有的引用,通过调用 <a class="reference internal" href="../c-api/refcounting.html#c.Py_INCREF" title="Py_INCREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_INCREF()</span></code></a> 。</p>
|
||
<p>Python调用从C函数返回的对象引用时必须是拥有的引用---拥有关系被从函数传递给调用者。</p>
|
||
</section>
|
||
<section id="thin-ice">
|
||
<span id="thinice"></span><h3><span class="section-number">1.10.3. </span>危险的薄冰<a class="headerlink" href="#thin-ice" title="永久链接至标题">¶</a></h3>
|
||
<p>有少数情况下,借用的引用看起来无害,但却可能导致问题。这通常是因为解释器的隐式调用,并可能导致引用拥有者处置这个引用。</p>
|
||
<p>首先需要特别注意的情况是使用 <a class="reference internal" href="../c-api/refcounting.html#c.Py_DECREF" title="Py_DECREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_DECREF()</span></code></a> 到一个无关对象,而这个对象的引用是借用自一个列表的元素。举个实例:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span>
|
||
<span class="nf">bug</span><span class="p">(</span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">list</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">item</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyList_GetItem</span><span class="p">(</span><span class="n">list</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span>
|
||
|
||
<span class="w"> </span><span class="n">PyList_SetItem</span><span class="p">(</span><span class="n">list</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">PyLong_FromLong</span><span class="p">(</span><span class="mf">0L</span><span class="p">));</span>
|
||
<span class="w"> </span><span class="n">PyObject_Print</span><span class="p">(</span><span class="n">item</span><span class="p">,</span><span class="w"> </span><span class="n">stdout</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span><span class="w"> </span><span class="cm">/* BUG! */</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这个函数首先借用一个引用 <code class="docutils literal notranslate"><span class="pre">list[0]</span></code> ,然后替换 <code class="docutils literal notranslate"><span class="pre">list[1]</span></code> 为值 <code class="docutils literal notranslate"><span class="pre">0</span></code> ,最后打印借用的引用。看起来无害是吧,但却不是。</p>
|
||
<p>我们跟着控制流进入 <a class="reference internal" href="../c-api/list.html#c.PyList_SetItem" title="PyList_SetItem"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyList_SetItem()</span></code></a> 。列表拥有者引用了其所有成员,所以当成员1被替换时,就必须处置原来的成员1。现在假设原来的成员1是用户定义类的实例,且假设这个类定义了 <a class="reference internal" href="../reference/datamodel.html#object.__del__" title="object.__del__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__del__()</span></code></a> 方法。如果这个类实例的引用计数是1,那么处置动作就会调用 <a class="reference internal" href="../reference/datamodel.html#object.__del__" title="object.__del__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__del__()</span></code></a> 方法。</p>
|
||
<p>既然是Python写的, <a class="reference internal" href="../reference/datamodel.html#object.__del__" title="object.__del__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__del__()</span></code></a> 方法可以执行任意Python代码。是否可能在 <code class="xref c c-func docutils literal notranslate"><span class="pre">bug()</span></code> 的 <code class="docutils literal notranslate"><span class="pre">item</span></code> 废止引用呢,是的。假设列表传递到 <code class="xref c c-func docutils literal notranslate"><span class="pre">bug()</span></code> 会被 <a class="reference internal" href="../reference/datamodel.html#object.__del__" title="object.__del__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__del__()</span></code></a> 方法所访问,就可以执行一个语句来实现 <code class="docutils literal notranslate"><span class="pre">del</span> <span class="pre">list[0]</span></code> ,然后假设这是最后一个对对象的引用,就需要释放内存,从而使得 <code class="docutils literal notranslate"><span class="pre">item</span></code> 无效化。</p>
|
||
<p>解决方法是,当你知道了问题的根源,就容易了:临时增加引用计数。正确版本的函数代码如下:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span>
|
||
<span class="nf">no_bug</span><span class="p">(</span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">list</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">item</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyList_GetItem</span><span class="p">(</span><span class="n">list</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span>
|
||
|
||
<span class="w"> </span><span class="n">Py_INCREF</span><span class="p">(</span><span class="n">item</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">PyList_SetItem</span><span class="p">(</span><span class="n">list</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">PyLong_FromLong</span><span class="p">(</span><span class="mf">0L</span><span class="p">));</span>
|
||
<span class="w"> </span><span class="n">PyObject_Print</span><span class="p">(</span><span class="n">item</span><span class="p">,</span><span class="w"> </span><span class="n">stdout</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">Py_DECREF</span><span class="p">(</span><span class="n">item</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这是个真实的故事。一个旧版本的Python包含了这个bug的变种,而一些人花费了大量时间在C调试器上去寻找为什么 <a class="reference internal" href="../reference/datamodel.html#object.__del__" title="object.__del__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__del__()</span></code></a> 方法会失败。</p>
|
||
<p>这个问题的第二种情况是借用的引用涉及线程的变种。通常,Python解释器里多个线程无法进入对方的路径,因为有个全局锁保护着Python整个对象空间。但可以使用宏 <a class="reference internal" href="../c-api/init.html#c.Py_BEGIN_ALLOW_THREADS" title="Py_BEGIN_ALLOW_THREADS"><code class="xref c c-macro docutils literal notranslate"><span class="pre">Py_BEGIN_ALLOW_THREADS</span></code></a> 来临时释放这个锁,重新获取锁用 <a class="reference internal" href="../c-api/init.html#c.Py_END_ALLOW_THREADS" title="Py_END_ALLOW_THREADS"><code class="xref c c-macro docutils literal notranslate"><span class="pre">Py_END_ALLOW_THREADS</span></code></a> 。这通常围绕在阻塞I/O调用外,使得其他线程可以在等待I/O期间使用处理器。显然,如下函数会跟之前那个有一样的问题:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="kt">void</span>
|
||
<span class="nf">bug</span><span class="p">(</span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">list</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">item</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyList_GetItem</span><span class="p">(</span><span class="n">list</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">Py_BEGIN_ALLOW_THREADS</span>
|
||
<span class="w"> </span><span class="p">...</span><span class="n">some</span><span class="w"> </span><span class="n">blocking</span><span class="w"> </span><span class="n">I</span><span class="o">/</span><span class="n">O</span><span class="w"> </span><span class="n">call</span><span class="p">...</span>
|
||
<span class="w"> </span><span class="n">Py_END_ALLOW_THREADS</span>
|
||
<span class="w"> </span><span class="n">PyObject_Print</span><span class="p">(</span><span class="n">item</span><span class="p">,</span><span class="w"> </span><span class="n">stdout</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span><span class="w"> </span><span class="cm">/* BUG! */</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="null-pointers">
|
||
<span id="nullpointers"></span><h3><span class="section-number">1.10.4. </span>NULL指针<a class="headerlink" href="#null-pointers" title="永久链接至标题">¶</a></h3>
|
||
<p>通常,接受对象引用作为参数的函数不希望你传给它们 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 指针,并且当你这样做时将会转储核心(或在以后导致核心转储)。 返回对象引用的函数通常只在要指明发生了异常时才返回 <code class="docutils literal notranslate"><span class="pre">NULL</span></code>。 不检测 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 参数的原因在于这些函数经常要将它们所接收的对象传给其他函数 --- 如果每个函数都检测 <code class="docutils literal notranslate"><span class="pre">NULL</span></code>,将会导致大量的冗余检测而使代码运行得更缓慢。</p>
|
||
<p>更好的做法是仅在“源头”上检测 <code class="docutils literal notranslate"><span class="pre">NULL</span></code>,即在接收到一个可能为 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 的指针,例如来自 <code class="xref c c-func docutils literal notranslate"><span class="pre">malloc()</span></code> 或是一个可能引发异常的函数的时候。</p>
|
||
<p><a class="reference internal" href="../c-api/refcounting.html#c.Py_INCREF" title="Py_INCREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_INCREF()</span></code></a> 和 <a class="reference internal" href="../c-api/refcounting.html#c.Py_DECREF" title="Py_DECREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_DECREF()</span></code></a> 等宏不会检测 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 指针 --- 但是,它们的变种 <a class="reference internal" href="../c-api/refcounting.html#c.Py_XINCREF" title="Py_XINCREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_XINCREF()</span></code></a> 和 <a class="reference internal" href="../c-api/refcounting.html#c.Py_XDECREF" title="Py_XDECREF"><code class="xref c c-func docutils literal notranslate"><span class="pre">Py_XDECREF()</span></code></a> 则会检测。</p>
|
||
<p>用于检测特定对象类型的宏 (<code class="docutils literal notranslate"><span class="pre">Pytype_Check()</span></code>) 不会检测 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 指针 --- 同样地,有大量代码会连续调用这些宏来测试一个对象是否为几种不同预期类型之一,这将会生成冗余的测试。 不存在带有 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 检测的变体。</p>
|
||
<p>C 函数调用机制会保证传给 C 函数的参数列表 (本示例中为 <code class="docutils literal notranslate"><span class="pre">args</span></code>) 绝不会为 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> --- 实际上它会保证其总是为一个元组 <a class="footnote-reference brackets" href="#id8" id="id4">4</a>。</p>
|
||
<p>任何时候将 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 指针“泄露”给 Python 用户都会是个严重的错误。</p>
|
||
</section>
|
||
</section>
|
||
<section id="writing-extensions-in-c">
|
||
<span id="cplusplus"></span><h2><span class="section-number">1.11. </span>在C++中编写扩展<a class="headerlink" href="#writing-extensions-in-c" title="永久链接至标题">¶</a></h2>
|
||
<p>还可以在C++中编写扩展模块,只是有些限制。如果主程序(Python解释器)是使用C编译器来编译和链接的,全局或静态对象的构造器就不能使用。而如果是C++编译器来链接的就没有这个问题。函数会被Python解释器调用(通常就是模块初始化函数)必须声明为 <code class="docutils literal notranslate"><span class="pre">extern</span> <span class="pre">"C"</span></code> 。而是否在 <code class="docutils literal notranslate"><span class="pre">extern</span> <span class="pre">"C"</span> <span class="pre">{...}</span></code> 里包含Python头文件则不是那么重要,因为如果定义了符号 <code class="docutils literal notranslate"><span class="pre">__cplusplus</span></code> 则已经是这么声明的了(所有现代C++编译器都会定义这个符号)。</p>
|
||
</section>
|
||
<section id="providing-a-c-api-for-an-extension-module">
|
||
<span id="using-capsules"></span><h2><span class="section-number">1.12. </span>给扩展模块提供C API<a class="headerlink" href="#providing-a-c-api-for-an-extension-module" title="永久链接至标题">¶</a></h2>
|
||
<p>很多扩展模块提供了新的函数和类型供Python使用,但有时扩展模块里的代码也可以被其他扩展模块使用。例如,一个扩展模块可以实现一个类型 "collection" 看起来是没有顺序的。就像是Python列表类型,拥有C API允许扩展模块来创建和维护列表,这个新的集合类型可以有一堆C函数用于给其他扩展模块直接使用。</p>
|
||
<p>开始看起来很简单:只需要编写函数(无需声明为 <code class="docutils literal notranslate"><span class="pre">static</span></code> ),提供恰当的头文件,以及C API的文档。实际上在所有扩展模块都是静态链接到Python解释器时也是可以正常工作的。当模块以共享库链接时,一个模块中的符号定义对另一个模块不可见。可见的细节依赖于操作系统,一些系统的Python解释器使用全局命名空间(例如Windows),有些则在链接时需要一个严格的已导入符号列表(一个例子是AIX),或者提供可选的不同策略(如Unix系列)。即便是符号是全局可见的,你要调用的模块也可能尚未加载。</p>
|
||
<p>可移植性需要不能对符号可见性做任何假设。这意味着扩展模块里的所有符号都应该声明为 <code class="docutils literal notranslate"><span class="pre">static</span></code> ,除了模块的初始化函数,来避免与其他扩展模块的命名冲突(在段落 <a class="reference internal" href="#methodtable"><span class="std std-ref">模块方法表和初始化函数</span></a> 中讨论) 。这意味着符号应该 <em>必须</em> 通过其他导出方式来供其他扩展模块访问。</p>
|
||
<p>Python提供了一个特别的机制来传递C级别信息(指针),从一个扩展模块到另一个:Capsules。一个Capsule是一个Python数据类型,会保存指针( <code class="xref c c-type docutils literal notranslate"><span class="pre">void</span> <span class="pre">*</span></code> )。Capsule只能通过其C API来创建和访问,但可以像其他Python对象一样的传递。通常,我们可以指定一个扩展模块命名空间的名字。其他扩展模块可以导入这个模块,获取这个名字的值,然后从Capsule获取指针。</p>
|
||
<p>Capsule可以用多种方式导出C API给扩展模块。每个函数可以用自己的Capsule,或者所有C API指针可以存储在一个数组里,数组地址再发布给Capsule。存储和获取指针也可以用多种方式,供客户端模块使用。</p>
|
||
<p>无论你选择哪个方法,正确地为你的 Capsule 命名都很重要。 函数 <a class="reference internal" href="../c-api/capsule.html#c.PyCapsule_New" title="PyCapsule_New"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyCapsule_New()</span></code></a> 接受一个名称形参 (<code class="xref c c-type docutils literal notranslate"><span class="pre">const</span> <span class="pre">char</span> <span class="pre">*</span></code>);允许你传入一个 <code class="docutils literal notranslate"><span class="pre">NULL</span></code> 作为名称,但我们强烈建议你指定一个名称。 正确地命名的 Capsule 提供了一定程序的运行时类型安全;没有可行的方式能区分两个未命名的 Capsule。</p>
|
||
<p>通常来说,Capsule用于暴露C API,其名字应该遵循如下规范:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">modulename</span><span class="p">.</span><span class="n">attributename</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>便利函数 <a class="reference internal" href="../c-api/capsule.html#c.PyCapsule_Import" title="PyCapsule_Import"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyCapsule_Import()</span></code></a> 可以方便的载入通过Capsule提供的C API,仅在Capsule的名字匹配时。这个行为为C API用户提供了高度的确定性来载入正确的C API。</p>
|
||
<p>如下例子展示了将大部分负担交由导出模块作者的方法,适用于常用的库模块。其会存储所有C API指针(例子里只有一个)在 <code class="xref c c-type docutils literal notranslate"><span class="pre">void</span></code> 指针的数组里,并使其值变为Capsule。对应的模块头文件提供了宏来管理导入模块和获取C API指针;客户端模块只需要在访问C API前调用这个宏即可。</p>
|
||
<p>导出的模块修改自 <code class="xref py py-mod docutils literal notranslate"><span class="pre">spam</span></code> 模块,来自 <a class="reference internal" href="#extending-simpleexample"><span class="std std-ref">一个简单的例子</span></a> 段落。函数 <code class="xref py py-func docutils literal notranslate"><span class="pre">spam.system()</span></code> 不会直接调用C库函数 <code class="xref c c-func docutils literal notranslate"><span class="pre">system()</span></code> ,但一个函数 <code class="xref c c-func docutils literal notranslate"><span class="pre">PySpam_System()</span></code> 会负责调用,当然现实中会更复杂些(例如添加 "spam" 到每个命令)。函数 <code class="xref c c-func docutils literal notranslate"><span class="pre">PySpam_System()</span></code> 也会导出给其他扩展模块。</p>
|
||
<p>函数 <code class="xref c c-func docutils literal notranslate"><span class="pre">PySpam_System()</span></code> 是个纯C函数,声明 <code class="docutils literal notranslate"><span class="pre">static</span></code> 就像其他地方那样:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="kt">int</span>
|
||
<span class="nf">PySpam_System</span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">command</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">system</span><span class="p">(</span><span class="n">command</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>函数 <code class="xref c c-func docutils literal notranslate"><span class="pre">spam_system()</span></code> 按照如下方式修改:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span>
|
||
<span class="nf">spam_system</span><span class="p">(</span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">command</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">sts</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="n">PyArg_ParseTuple</span><span class="p">(</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="s">"s"</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">command</span><span class="p">))</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="n">sts</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PySpam_System</span><span class="p">(</span><span class="n">command</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">PyLong_FromLong</span><span class="p">(</span><span class="n">sts</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>在模块开头,在此行后:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cp">#include</span><span class="w"> </span><span class="cpf"><Python.h></span>
|
||
</pre></div>
|
||
</div>
|
||
<p>添加另外两行:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cp">#define SPAM_MODULE</span>
|
||
<span class="cp">#include</span><span class="w"> </span><span class="cpf">"spammodule.h"</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">#define</span></code> 用于告知头文件需要包含给导出的模块,而不是客户端模块。最终,模块的初始化函数必须负责初始化C API指针数组:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">PyMODINIT_FUNC</span>
|
||
<span class="nf">PyInit_spam</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">m</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="o">*</span><span class="n">PySpam_API</span><span class="p">[</span><span class="n">PySpam_API_pointers</span><span class="p">];</span>
|
||
<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">c_api_object</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyModule_Create</span><span class="p">(</span><span class="o">&</span><span class="n">spammodule</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="cm">/* Initialize the C API pointer array */</span>
|
||
<span class="w"> </span><span class="n">PySpam_API</span><span class="p">[</span><span class="n">PySpam_System_NUM</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">void</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">PySpam_System</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="cm">/* Create a Capsule containing the API pointer array's address */</span>
|
||
<span class="w"> </span><span class="n">c_api_object</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyCapsule_New</span><span class="p">((</span><span class="kt">void</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="n">PySpam_API</span><span class="p">,</span><span class="w"> </span><span class="s">"spam._C_API"</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">);</span>
|
||
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">PyModule_AddObject</span><span class="p">(</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="s">"_C_API"</span><span class="p">,</span><span class="w"> </span><span class="n">c_api_object</span><span class="p">)</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="n">Py_XDECREF</span><span class="p">(</span><span class="n">c_api_object</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="n">Py_DECREF</span><span class="p">(</span><span class="n">m</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">m</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>注意 <code class="docutils literal notranslate"><span class="pre">PySpam_API</span></code> 声明为 <code class="docutils literal notranslate"><span class="pre">static</span></code> ;此外指针数组会在 <code class="xref py py-func docutils literal notranslate"><span class="pre">PyInit_spam()</span></code> 结束后消失!</p>
|
||
<p>头文件 <code class="file docutils literal notranslate"><span class="pre">spammodule.h</span></code> 里的一堆工作,看起来如下所示:</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="cp">#ifndef Py_SPAMMODULE_H</span>
|
||
<span class="cp">#define Py_SPAMMODULE_H</span>
|
||
<span class="cp">#ifdef __cplusplus</span>
|
||
<span class="k">extern</span><span class="w"> </span><span class="s">"C"</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="cp">#endif</span>
|
||
|
||
<span class="cm">/* Header file for spammodule */</span>
|
||
|
||
<span class="cm">/* C API functions */</span>
|
||
<span class="cp">#define PySpam_System_NUM 0</span>
|
||
<span class="cp">#define PySpam_System_RETURN int</span>
|
||
<span class="cp">#define PySpam_System_PROTO (const char *command)</span>
|
||
|
||
<span class="cm">/* Total number of C API pointers */</span>
|
||
<span class="cp">#define PySpam_API_pointers 1</span>
|
||
|
||
|
||
<span class="cp">#ifdef SPAM_MODULE</span>
|
||
<span class="cm">/* This section is used when compiling spammodule.c */</span>
|
||
|
||
<span class="k">static</span><span class="w"> </span><span class="n">PySpam_System_RETURN</span><span class="w"> </span><span class="n">PySpam_System</span><span class="w"> </span><span class="n">PySpam_System_PROTO</span><span class="p">;</span>
|
||
|
||
<span class="cp">#else</span>
|
||
<span class="cm">/* This section is used in modules that use spammodule's API */</span>
|
||
|
||
<span class="k">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="o">**</span><span class="n">PySpam_API</span><span class="p">;</span>
|
||
|
||
<span class="cp">#define PySpam_System \</span>
|
||
<span class="cp"> (*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])</span>
|
||
|
||
<span class="cm">/* Return -1 on error, 0 on success.</span>
|
||
<span class="cm"> * PyCapsule_Import will set an exception if there's an error.</span>
|
||
<span class="cm"> */</span>
|
||
<span class="k">static</span><span class="w"> </span><span class="kt">int</span>
|
||
<span class="nf">import_spam</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PySpam_API</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="kt">void</span><span class="w"> </span><span class="o">**</span><span class="p">)</span><span class="n">PyCapsule_Import</span><span class="p">(</span><span class="s">"spam._C_API"</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">(</span><span class="n">PySpam_API</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="mi">-1</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="cp">#endif</span>
|
||
|
||
<span class="cp">#ifdef __cplusplus</span>
|
||
<span class="p">}</span>
|
||
<span class="cp">#endif</span>
|
||
|
||
<span class="cp">#endif </span><span class="cm">/* !defined(Py_SPAMMODULE_H) */</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>客户端模块必须在其初始化函数里按顺序调用函数 <code class="xref c c-func docutils literal notranslate"><span class="pre">import_spam()</span></code> (或其他宏)才能访问函数 <code class="xref c c-func docutils literal notranslate"><span class="pre">PySpam_System()</span></code> 。</p>
|
||
<div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">PyMODINIT_FUNC</span>
|
||
<span class="nf">PyInit_client</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">m</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyModule_Create</span><span class="p">(</span><span class="o">&</span><span class="n">clientmodule</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">import_spam</span><span class="p">()</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="w"> </span><span class="cm">/* additional initialization can happen here */</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">m</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>这种方法的主要缺点是,文件 <code class="file docutils literal notranslate"><span class="pre">spammodule.h</span></code> 过于复杂。当然,对每个要导出的函数,基本结构是相似的,所以只需要学习一次。</p>
|
||
<p>最后需要提醒的是Capsule提供了额外的功能,用于存储在Capsule里的指针的内存分配和释放。细节参考 Python/C API参考手册的章节 <a class="reference internal" href="../c-api/capsule.html#capsules"><span class="std std-ref">胶囊</span></a> 和Capsule的实现(在Python源码发行包的 <code class="file docutils literal notranslate"><span class="pre">Include/pycapsule.h</span></code> 和 <code class="file docutils literal notranslate"><span class="pre">Objects/pycapsule.c</span></code> )。</p>
|
||
<p class="rubric">备注</p>
|
||
<dl class="footnote brackets">
|
||
<dt class="label" id="id5"><span class="brackets"><a class="fn-backref" href="#id1">1</a></span></dt>
|
||
<dd><p>这个函数的接口已经在标准模块 <a class="reference internal" href="../library/os.html#module-os" title="os: Miscellaneous operating system interfaces."><code class="xref py py-mod docutils literal notranslate"><span class="pre">os</span></code></a> 里了,这里作为一个简单而直接的例子。</p>
|
||
</dd>
|
||
<dt class="label" id="id6"><span class="brackets"><a class="fn-backref" href="#id2">2</a></span></dt>
|
||
<dd><p>术语"借用"一个引用是不完全正确的:拥有者仍然有引用的拷贝。</p>
|
||
</dd>
|
||
<dt class="label" id="id7"><span class="brackets"><a class="fn-backref" href="#id3">3</a></span></dt>
|
||
<dd><p>检查引用计数至少为1 <strong>没有用</strong> ,引用计数本身可以在已经释放的内存里,并有可能被其他对象所用。</p>
|
||
</dd>
|
||
<dt class="label" id="id8"><span class="brackets"><a class="fn-backref" href="#id4">4</a></span></dt>
|
||
<dd><p>当你使用 "旧式" 风格调用约定时,这些保证不成立,尽管这依旧存在于很多旧代码中。</p>
|
||
</dd>
|
||
</dl>
|
||
</section>
|
||
</section>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||
<div class="sphinxsidebarwrapper">
|
||
<h3><a href="../contents.html">目录</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">1. 使用 C 或 C++ 扩展 Python</a><ul>
|
||
<li><a class="reference internal" href="#a-simple-example">1.1. 一个简单的例子</a></li>
|
||
<li><a class="reference internal" href="#intermezzo-errors-and-exceptions">1.2. 关于错误和异常</a></li>
|
||
<li><a class="reference internal" href="#back-to-the-example">1.3. 回到例子</a></li>
|
||
<li><a class="reference internal" href="#the-module-s-method-table-and-initialization-function">1.4. 模块方法表和初始化函数</a></li>
|
||
<li><a class="reference internal" href="#compilation-and-linkage">1.5. 编译和链接</a></li>
|
||
<li><a class="reference internal" href="#calling-python-functions-from-c">1.6. 在C中调用Python函数</a></li>
|
||
<li><a class="reference internal" href="#extracting-parameters-in-extension-functions">1.7. 提取扩展函数的参数</a></li>
|
||
<li><a class="reference internal" href="#keyword-parameters-for-extension-functions">1.8. 给扩展函数的关键字参数</a></li>
|
||
<li><a class="reference internal" href="#building-arbitrary-values">1.9. 构造任意值</a></li>
|
||
<li><a class="reference internal" href="#reference-counts">1.10. 引用计数</a><ul>
|
||
<li><a class="reference internal" href="#reference-counting-in-python">1.10.1. Python中的引用计数</a></li>
|
||
<li><a class="reference internal" href="#ownership-rules">1.10.2. 拥有规则</a></li>
|
||
<li><a class="reference internal" href="#thin-ice">1.10.3. 危险的薄冰</a></li>
|
||
<li><a class="reference internal" href="#null-pointers">1.10.4. NULL指针</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#writing-extensions-in-c">1.11. 在C++中编写扩展</a></li>
|
||
<li><a class="reference internal" href="#providing-a-c-api-for-an-extension-module">1.12. 给扩展模块提供C API</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<h4>上一个主题</h4>
|
||
<p class="topless"><a href="index.html"
|
||
title="上一章">扩展和嵌入 Python 解释器</a></p>
|
||
<h4>下一个主题</h4>
|
||
<p class="topless"><a href="newtypes_tutorial.html"
|
||
title="下一章"><span class="section-number">2. </span>自定义扩展类型:教程</a></p>
|
||
<div role="note" aria-label="source link">
|
||
<h3>本页</h3>
|
||
<ul class="this-page-menu">
|
||
<li><a href="../bugs.html">报告 Bug</a></li>
|
||
<li>
|
||
<a href="https://github.com/python/cpython/blob/3.8/Doc/extending/extending.rst"
|
||
rel="nofollow">显示源代码
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></div>
|
||
</div>
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>导航</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="总目录"
|
||
>索引</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python 模块索引"
|
||
>模块</a> |</li>
|
||
<li class="right" >
|
||
<a href="newtypes_tutorial.html" title="2. 自定义扩展类型:教程"
|
||
>下一页</a> |</li>
|
||
<li class="right" >
|
||
<a href="index.html" title="扩展和嵌入 Python 解释器"
|
||
>上一页</a> |</li>
|
||
|
||
<li><img src="../_static/py.svg" alt="python logo" style="vertical-align: middle; margin-top: -1px"/></li>
|
||
<li><a href="https://www.python.org/">Python</a> »</li>
|
||
<li class="switchers">
|
||
<div class="language_switcher_placeholder"></div>
|
||
<div class="version_switcher_placeholder"></div>
|
||
</li>
|
||
<li>
|
||
|
||
</li>
|
||
<li id="cpython-language-and-version">
|
||
<a href="../index.html">3.8.20 Documentation</a> »
|
||
</li>
|
||
|
||
<li class="nav-item nav-item-1"><a href="index.html" >扩展和嵌入 Python 解释器</a> »</li>
|
||
<li class="right">
|
||
|
||
|
||
<div class="inline-search" role="search">
|
||
<form class="inline-search" action="../search.html" method="get">
|
||
<input placeholder="快速搜索" aria-label="快速搜索" type="text" name="q" />
|
||
<input type="submit" value="转向" />
|
||
<input type="hidden" name="check_keywords" value="yes" />
|
||
<input type="hidden" name="area" value="default" />
|
||
</form>
|
||
</div>
|
||
|
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
<div class="footer">
|
||
© <a href="../copyright.html">版权所有</a> 2001-2024, Python Software Foundation.
|
||
<br />
|
||
This page is licensed under the Python Software Foundation License Version 2.
|
||
<br />
|
||
Examples, recipes, and other code in the documentation are additionally licensed under the Zero Clause BSD License.
|
||
<br />
|
||
|
||
<br />
|
||
|
||
The Python Software Foundation is a non-profit corporation.
|
||
<a href="https://www.python.org/psf/donations/">Please donate.</a>
|
||
<br />
|
||
<br />
|
||
|
||
最后更新于 12月 09, 2024.
|
||
<a href="https://docs.python.org/3/bugs.html">Found a bug</a>?
|
||
<br />
|
||
|
||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 2.4.4.
|
||
</div>
|
||
|
||
</body>
|
||
</html> |