Welcome to use python documentation!

Contents:

python包管理

Page Status:Development
Last Reviewed:

python的一大优点是第三方库的丰富易用

我们将在 打包并发布 一章中介绍python库的结构,在这边我们先介绍如果获取和管理第三方库

PyPI (别名: 奶酪商店)

PyPI是python官方的第三方库的仓库,所有人都可以下载第三方库或上传自己开发的库到PyPI

PyPI推荐使用pip包管理器来下载第三方库。

手工下载安装库

直接下载并解压库的zip或者tar.gz包,运行命令 python setup.py install

注解

如果提示找不到setuptools,需要先安装setuptools库

注解

这种安装方法不会自动安装库的依赖

使用easy_install管理第三方库

安装 setuptools

可以直接下载压缩包,解压安装。或者对于联网的机器,也可以使用 ez_setup.py 安装:

python ez_setup.py

安装 setuptools 结束之后, easy_install也会被同时安装。 将easy_install的路径加入执行路径后,在console中就可以使用easy_install来安装python包,easy_install支持离线安装(zip包,tar包,exe包)和在线安装(PyPI URL),并且能自动下载安装依赖包:

easy_install [options] package_pat/URL

注解

对于使用代理的机器,在使用easy_install之前需要先设置console的环境变量来指明代理

set http_proxy=...

set https_proxy=...

使用pip管理第三方库(推荐)

安装pip

pip已经在python 3.4中默认包含,如果使用的是其他的python版本,需要先安装pip,三种方法:

  1. 下载 pip安装包 安装
  2. easy_install pip
  3. 下载 get-pip.py , 执行命令 python get-pip.py

使用pip

pip的用法和easy_install类似,但支持更多的功能:

Usage:
  pip <command> [options]

Commands:
  install                     Install packages.
  uninstall                   Uninstall packages.
  freeze                      Output installed packages in requirements format.
  list                        List installed packages.
  show                        Show information about installed packages.
  search                      Search PyPI for packages.
  wheel                       Build wheels from your requirements.
  zip                         DEPRECATED. Zip individual packages.
  unzip                       DEPRECATED. Unzip individual packages.
  help                        Show help for commands.

General Options:
  -h, --help                  Show help.
  --isolated                  Run pip in an isolated mode, ignoring
                              environment variables and user configuration.
  -v, --verbose               Give more output. Option is additive, and can be
                              used up to 3 times.
  -V, --version               Show version and exit.
  -q, --quiet                 Give less output.
  --log <path>                Path to a verbose appending log.
  --proxy <proxy>             Specify a proxy in the form
                              [user:passwd@]proxy.server:port.
  --retries <retries>         Maximum number of retries each connection should
                              attempt (default 5 times).
  --timeout <sec>             Set the socket timeout (default 15 seconds).
  --exists-action <action>    Default action when a path already exists:
                              (s)witch, (i)gnore, (w)ipe, (b)ackup.
  --trusted-host <hostname>   Mark this host as trusted, even though it does
                              not have valid or any HTTPS.
  --cert <path>               Path to alternate CA bundle.
  --client-cert <path>        Path to SSL client certificate, a single file
                              containing the private key and the certificate
                              in PEM format.
  --cache-dir <dir>           Store the cache data in <dir>.
  --no-cache-dir              Disable the cache.
  --disable-pip-version-check
                              Don't periodically check PyPI to determine
                              whether a new version of pip is available for
                              download. Implied with --no-index.

注解

对于使用代理的机器,在使用pip时候需要指明代理

pip install XXX –proxy <proxy>

python编程模式

本章介绍python编程中常用到的一些模式,面向有一定python编程经验的程序员,来提高和完善自己的python知识。

初学者最好首先参考一本python入门读本,推荐O’REILLY出版的 learning python

logging模块的使用

logging模块的使用能大大增强程序的可调试性,建议所有的python应用和库都使用logging来输出信息。

参见 python guide – logging

everything is object

Python是一门动态语言,所有的类型(函数,类,实例)都是一个object,可以被赋值给任意的变量,给python编程带来非常大的灵活性。

代码示例,动态调用函数:

functions = {
    "func1": func1,
    "func2": func2,
}

def func1():
    print(1)

def func2():
    print(2)

if __name__=="__main__":
    import sys
    func_name = sys.argv[1]
    functions[func_name]()

迭代器和组合操作

迭代器和组合操作是python 从Haskell借鉴的语法

迭代器

迭代器可以有多种实现方式:

迭代器的使用:

组合操作

组合操作比一般的循环操作节省代码,而且能提高性能,组合操作会使用C的循环,而不是一般的python循环,python推荐使用组合操作来生成列表。

组合操作形式:

[ [expression] for object in iter_object [for_or_if_expression] ]

示例1:

>>> res = []
>>> for line in open('script1.py'):
... if line[0] == 'p':
... res.append(line.rstrip())
...
>>> res
['print(sys.path)', 'print(2 ** 33)']
>>> lines = [line.rstrip() for line in open('script1.py') if line[0] == 'p']
>>> lines
['print(sys.path)', 'print(2 ** 33)']

示例2:

>>> res = []
>>> for x in 'abc':
... for y in 'lmn':
... res.append(x + y)
...
>>> res
['al', 'am', 'an', 'bl', 'bm', 'bn', 'cl', 'cm', 'cn']
>>> [x + y for x in 'abc' for y in 'lmn']
['al', 'am', 'an', 'bl', 'bm', 'bn', 'cl', 'cm', 'cn']

动态加载模块

Python通过 __import__ 函数支持动态加载模块

动态加载os模块:

os_module = __import__("os")

动态加载os.path模块:

os_path_module = __import__("os.path", fromlist=["join"])

注解

如果没有fromlist参数,即使模块名字是 os.path,也只会返回os模块:

os_module = __import__("os.path")

注解

在python 2.7和python 3之后,也可以使用 importlib.import_module() 来动态加载模块

return VS exception

关于使用返回值还是使用异常的建议:

  1. 我们应该对使用返回值的情景和使用异常的情景进行区分,使用返回值来表达函数的状态是不推荐的,会导致上层编码风格的混乱
  2. 只使用返回值来传递数据,如果函数没有想要返回的值,尽量不要在函数中使用return,python会默认返回None
  3. 使用具体的异常类型,比如built-in的 ValueError, AttributeError, 不要使用 Exception, 如果需要自定义异常,将自定义的异常统一放到一个模块中,这样上层代码能方便访问你的自定义异常
  4. 尽量统一在上层处理异常,中间层尽量不处理异常,让异常扩散到统一处理异常的地方

property VS method

关于使用属性还是方法的建议:

  1. 属性一般意味着从内存中直接拿出之前存储的值
  2. 方法意味着需要一定的处理
  3. 如果设计上想让外部以为是属性,但需要一定的内部处理,可以使用 @property 修饰

Unicode

这个关于unicode的章节是针对python 2的。

hex转义

str类型可以存储所有的ascii码的字符,比如英文字母,数字和一些标点符号。但对于其他语种里面的字符,比如中文字符,则需要特殊的存储处理。

str类型对于非ascii的字符,是直接存储该字符的二进制值(使用某种编码格式),可以用hex转义的str来存储这种二进制值。

示例代码如下,解析器的编码格式是UTF-8:

>>> a = "你好"
>>> a
'\xe4\xbd\xa0\xe5\xa5\xbd'
>>> c = '\xe4\xbd\xa0\xe5\xa5\xbd'
>>> print(c)
你好

编解码(encode & decode)

编码将unicode转化成某种编码格式,编码将某种编码格式转化成unicode,标准编码格式见 Standard Encodings

一般国际通用的编码格式是UTF-8,中文有时候会用gb2312,gbk编码,推荐使用UTF-8的编码格式。

示例代码如下,解析器的编码格式是UTF-8:

>>> a = "你好"
>>> a
'\xe4\xbd\xa0\xe5\xa5\xbd'
>>> b = a.decode("utf-8")
>>> b
u'\u4f60\u597d'
>>> c = b.encode("utf-8")
>>> c
'\xe4\xbd\xa0\xe5\xa5\xbd'
>>> a == c
True

文本文件和解析器本身的编码

文本文件和解析器这种需要向用户显示信息的地方,一般都有自己的编码格式,能将二进制数据显示成文本,或将文本存储成二进制数据。

当编辑器载入文本的时候,需要知道文本的编码格式,才能正确显示。编辑器如何知道文本的编码格式呢,不同的文本格式的规则也有所不同。

  1. Windows文本文件会在文件开头添加一些额外的字节来标识不同的编码格式
  2. python脚本默认编码格式是UTF-8,可以在文本开头添加 # -*- coding: latin-1 -*- 来声明编码格式
  3. XML默认编码格式是UTF-8,可以在开头添加 <?xml version="1.0" encoding="ISO-8859-1"?> 来声明编码格式
  4. HTML默认编码格式是UTF-8,可以在开头添加 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 来声明编码格式

python解析器在不同平台上使用的编码格式也可能不同

Windows console:

>>> import locale
>>> locale.getdefaultlocale()
('en_US', 'cp1252')

Ubuntu terminal:

>>> import locale
>>> locale.getdefaultlocale()
('en_PH', 'UTF-8')

线程和全局锁(GIL)

GIL 使得python的解析器运行在一个单线程上,简化python的解析器的实现,提高性能。 代价是python中多线程不能把load分配给CPU的多核, 参见 Thread State and the Global Interpreter Lock

所以在python执行并行任务,可以尽量使用多进程来提高效率 multiprocessing

当然,在必须使用线程的情况下,比如UI应用,可以使用python提供的线程库 threading,但是不能利用CPU的多核来提高性能。

垃圾回收机制

Python有一个自动的垃圾回收机制,原理如下:

  • python解析器对所有的object做引用计数,每次垃圾回收时,没有被引用的object会被free
  • 当内存分配的object足够多的时候,python垃圾回收会自动运行,自动垃圾回收的频率可以通过 gc.set_threshold 设置
  • python垃圾回收机制将object分成三类, threshold0~threshold2, object的threshold越高, 执行自动垃圾回收的频率越低
  • python垃圾回收会释放循环引用的object
  • 循环引用,且包含__del__方法的object,不会被垃圾回收释放
  • 可以通过调用 gc.collect 手动运行垃圾回收

注解

频繁的垃圾回收对python应用的性能有明显影响,所以运行垃圾回收需要有一些注意:

  • 不要频繁的运行垃圾回收
  • 在不经常运行的代码后面加入手动垃圾回收
  • 在性能要求高的代码段不要运行垃圾回收,在这些代码段运行接收再进行垃圾回收

更多参考:

面向对象设计和编程

python提供了 一套面向对象的语言特性,来支持面向对象设计和编程

模板和接口

动态绑定

多重继承问题

python class支持多重继承,但需要注意的,使用多重继承时,python查找成员的机制。

  • 对于old-style的类(python 2中不继承object的类),查找成员的顺序是由左到右,深度优先
  • 对于new-style的类(python 2中继承object的类,python 3中所有的类都是new-style的), 查找顺序遵循 MRO(Method Resolution Order)

修饰器

元类

匿名函数

在python中使用设计模式

单元测试

Page Status:Development
Last Reviewed:

参见 Python guide, Testing Your Code

注解

Python guide, Testing Your Code 推荐的几个测试框架/工具中, 个人推荐 mock工具,对其他的unit test框架是否比python built-in的 unittest 框架好,我保留意见。

python代码质量

Page Status:Development
Last Reviewed:

python编码风格建议

参见 Python guide, Code Style

注解

关于使用返回值还是使用异常的建议:

  1. 我们应该对使用返回值的情景和使用异常的情景进行区分,使用返回值来表达函数的状态是不推荐的,会导致上层编码风格的混乱
  2. 只使用返回值来传递数据,如果函数没有想要返回的值,尽量不要在函数中使用return,python会默认返回None
  3. 使用具体的异常类型,比如built-in的 ValueError, AttributeError, 不要使用 Exception, 如果需要自定义异常,将自定义的异常统一放到一个模块中,这样上层代码能方便访问你的自定义异常
  4. 尽量统一在上层处理异常,中间层尽量不处理异常,让异常扩散到统一处理异常的地方

python编码检查工具

pylint

使用 pylint 相当简单,参见 A Beginner’s Guide to Code Standards in Python - Pylint Tutorial

注解

pylint 的默认设置有时候太严格了,我们可以通过创建pylint的configure文件来声明我们需要enable/disable哪些规则

  1. 首先,我们生成一份sample configure文件, pylint --generate-rcfile sample_config
  2. 在文件中找到 [MESSAGES CONTROL] 段,设置 enable= disable= 为需要打开/关闭的规则,规则列表见 pylint-checkers-options-and-switches
  3. configure文件需要放到合适的位置, 当前文件夹下的 pylintrc 文件 > 环境变量 PYLINTRC 指明的文件 > 用户目录下的 .pylintrc 文件 > /etc/pylintrc

landscape

一个python代码质量检查网站,对于开源工程是免费的。如果你的工程是一个host在GitHub上的开源的python工程,这个网站是一个很好的代码质量检查工具。

参考它的文档: landscape文档

打包并发布

Page Status:Development
Last Reviewed:

之前的一章我们介绍了如何下载和管理第三方库,这里我们介绍如何生成并发布我们自己的库

python库结构

一般的python库结构:

package folder -+
                |
                +setup.py        <--主要的配置/安装文件,必须
                |
                +setup.cfg       <--提供给用户的配置文件,可选
                |
                +README          <--readme文件,可选
                |
                +MANIFEST.in     <--打包配置文件模板,用来和setup.py一起生成打包配置文件MANIFEST,可选
                |
                +<your package>  <--你的package,最好与package folder名字一致
                |
                +<other files>   <--额外的文件

利用 distutils 打包

disutils 是python的标准库,提供了基本的工具集来打包和发布python库, 同时也确立了python库的基本结构。

我们可以利用 disutils 来快速的为我们自己的package构建一个标准的库,步奏如下:

1. 创建package同名文件夹
2. 将package放到文件夹中,同时新建文件setup.py
3. 编辑setup.py文件,从disutils.core中加载setup函数,并给setup函数添加参数(project信息)
4. 如果需要,添加MANIFEST.in,来处理setup.py打包时没有添加库里面的文件,见 `MANIFEST.in语法 <https://docs.python.org/3.4/distutils/sourcedist.html#specifying-the-files-to-distribute>`_
5. 运行命令python setup.py sdist生成库

setup函数的参数:

name
工程名字,名字中可以包含的字符见 `PEP426 <http://legacy.python.org/dev/peps/pep-0426/#name>`_

version
工程版本,格式见 `PEP440 <https://pypa.io/en/latest/peps/#pep440s>`_:

    1.2.0.dev1  # Development release
    1.2.0a1     # Alpha Release
    1.2.0b1     # Beta Release
    1.2.0rc1    # RC Release
    1.2.0       # Final Release
    1.2.0.post1 # Post Release

description
工程描述

url
工程地址

author
作者信息

license
许可证信息

classifiers
一组你的工程相关的信息,PyPI会利用这些信息来给你的工程分类,见 https://pypi.python.org/pypi?%3Aaction=list_classifiers

keywords
一组关键字来描述你的工程

packages
你的工程中包含的package,需要列出所需的所有package和subpackage,disutils不会自动寻找subpackage

install_requires
依赖列表,pip安装时候会根据这个列表来自动安装所需依赖库,比如sphinx的依赖列表::

    install_requires = [
    'six>=1.4',
    'Jinja2>=2.3',
    'Pygments>=1.2',
    'docutils>=0.10',
    'snowballstemmer>=1.1',
    'babel',
    ]

package_data
package中的数据列表,生成库的时候这个列表中的数据都会被加进MANIFEST文件
安装库的时候,这个列表中的数据会被安装

data_files
和package_data的区别在于,data_files列出的数据不在package中

scripts
entry_points
console_scripts

注解

如果使用python2.6或之前的版本,即使package_data中列出了文件,在MANIFEST.in仍然需要再添加一遍

利用 setuptools 打包 (推荐)

setuptools 是另一款第三方的打包发布工具集,兼容 disutils 的库结构

setuptools 的使用步骤和 disutils 类似,添加优化了一些功能,使得打包发布更加容易

主要增强的功能有:

  • 自动查找/下载/安装/升级库的依赖
  • 自动包含所有的packages,不需要全部列出了
  • 自动包含所有的相关的data文件,不需要新建一个MANIFEST.in文件了

新增或改变的setup函数的参数:

include_package_data
设成True,则自动添加你的工程目录中的所有的文件,如果没有额外的指明,只添加全部的文件

exclude_package_data
指明了需要排除的文件

package_data
指明了需要添加的文件

zip_safe
指明你的工程是否能够以压缩的格式安装

install_requires
依赖

注解

其他的关键字见 setuptools新增改变的关键字列表

注解

setuptools 通过find_packages函数来自动包含所有的packages,对于大型软件来说,极大的方便了packages的管理

find(cls, where='.', exclude=(), include=('*',))

method of __builtin__.type instance
Return a list all Python packages found within directory 'where'

'where' should be supplied as a "cross-platform" (i.e. URL-style)
path; it will be converted to the appropriate local path syntax.
'exclude' is a sequence of package names to exclude; '*' can be used
as a wildcard in the names, such that 'foo.*' will exclude all
subpackages of 'foo' (but not 'foo' itself).

'include' is a sequence of package names to include.  If it's
specified, only the named packages will be included.  If it's not
specified, all found packages will be included.  'include' can contain
shell style wildcard patterns just like 'exclude'.

The list of included packages is built up first and then any
explicitly excluded packages are removed from it.

注解

对于使用setuptools来打包的库,用户安装使用之前需要安装合适版本的setuptools

setuptools为本机没有安装setuptools的用户提供了一个解决方法,在安装库之前自动安装setuptools:

下载 ez_setup.py ,并包含在库根文件夹下

同时在setup.py中添加:

try:
    from setuptools import setup, find_packages
except ImportError:
    import ez_setup
    ez_setup.use_setuptools()
    from setuptools import setup, find_packages

上传库到PyPI

  1. 注册账号,建议通过 PyPI用户注册界面 完成注册
  2. 注册工程,进入工程目录,python setup.py register ,最后提示保存pypi账号的时候,确认保存
  3. 上传工程,建议使用 twine 来上传
twine upload dist/*

注解

在使用代理的机器上,需要首先设置console的环境变量来指明代理

set http_proxy=...

set https_proxy=...

编译成二进制文件

Page Status:Development
Last Reviewed:

对于某些应用,可能会需要将python应用打包成独立的可执行文件,这样可以发布给别人直接使用,不需要安装python或其他依赖,而且可以某种程度上保护源代码。 一般对于desktop app有这样的需求,对于web app,脚本,python库一般没有这样的需求。

下面介绍几个python工具来满足这种需求

py2exe

py2exe支持将python app转化成windows exe,支持python2.7和python3.3

py2exe for python2.7 host在 sourceforge上

py2exe for python3.3 host在 PyPI上

基本使用步奏

参考 py2exe Tutorial

  1. 安装py2exe
  2. 安装打包所需windows runtime, 对于python2.7和python3.3, 需要安装VS2008或 Microsoft Visual C++ 2008 Redistributable Package
  3. 编写或修改setup.py,setup.py中py2exe的参数参考 ListOfOptions
  4. 打包 python setup.py sdist py2exe

一个加入py2exe支持的setup.py,和普通的setup.py的比较:

_images/py2exe-setup.PNG

注解

如果安装完windows runtime,仍然提示 msvcp90.dll 找不到,你需要手动将 msvcm90.dll, msvcp90.dll, msvcr90.dll 拷贝到 C:\Python27\DLLs\

如果安装了VS2008,这些文件路径位于 C:\\Program Files\\Microsoft Visual Studio 9.0\\VC\\redist\\x86\\Microsoft.VC90.CRT

如果仅仅是安装了 Microsoft Visual C++ 2008 Redistributable Package, 这些文件路径一般类似于 C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.6161_none_50934f2ebcb7eb57

注解

如果你的python应用用了一些特殊的库,比如PyQt,可能会遇到一些问题,参见 Working with Various Packages and Modules

pyinstaller

pyinstaller支持将python app转化成windows/linux/mac上的可执行文件,支持python2.7,host在 PyPI上

python与其他语言交互

Page Status:Development
Last Reviewed:

python的一大特性就是能和很多语言方便的交互,所以python经常被称为胶水语言。

使用 ctypes 访问第三方C动态库 [1] [2]

Windows平台

在32bit的Windows系统上,对 __cdecl__stdcall 声明的函数,编译时函数名会使用不同的命名规则 [6]

比如编译如下C代码:

int _cdecl    f (int x) { return 0; }
int _stdcall  g (int y) { return 0; }
int _fastcall h (int z) { return 0; }

32bit的编译器会输出:

_f
_g@4
@h@4

cdll使用 __cdecl 声明函数, windll和oledll使用 __stdcall 声明函数

oledll比一般的windll额外指定了返回值的类型,为 HRESULT,见 OleDLL

所以ctypes为windows系统提供了三种dll的类来区分使用,windows上既可以通过属性来访问dll,也可以通过 cdll.LoadLibrary 来访问:

>>> from ctypes import *
>>> print windll.kernel32
<WinDLL 'kernel32', handle ... at ...>
>>> print cdll.msvcrt
<CDLL 'msvcrt', handle ... at ...>
>>> libc = cdll.msvcrt
>>>

Linux平台

linux平台上仅有cdll,linux平台上需要指明动态库的全名,通过 cdll.LoadLibrary 来访问:

>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>

注解

ctypes不能支持加载c++的动态库 [5]

使用 comtypes 访问COM组件 [7]

windows平台上大部分模块基于COM技术,对COM的支持使得python能支持大部分的windows上的模块

在没有comtypes的情况下,利用ole.dll中的C函数也可以实现对COM组件的访问 [8] ,但需要自己处理COM组件和python之间的数据结构的差异,编程效率低

下面是comtypes中访问COM组件的代码节选:

#COM functions are in ole32.dll
from ctypes import oledll
_ole32 = oledll.ole32
_ole32_nohresult = windll.ole32

#init COM
_ole32.CoInitializeEx(None, None)

#create COM object instance
_ole32.CoCreateInstance(byref(clsid), punkouter, clsctx, byref(iid), byref(p))

#use COM functions
p.func1()
p.func2()

#deinit COM
_ole32_nohresult.CoUninitialize()

注解

COM组件使用regsvr32.exe注册时,将COM组件的信息(dll的信息,clsid,progid)写入注册表,需要使用时再根据clsid或progid从注册表中查找dll的信息 [9]

既可以直接使用regedit来查看注册的COM组件信息,也可以用专门的工具oleview.exe (in WDK)来查看

利用comtypes,可以用纯python代码实现对所有类型COM组件的访问(custom类型的,支持dispatch接口类型的),并且支持编写COM组件

注解

pywin32 不支持对custom类型的COM组件的访问

通过clsid,progid,来访问COM组件

如果我们知道了COM组件的clsid或progid,可以通过comtypes.client中的CreateObject函数来访问COM组件:

instance = CreateObject(clsid/progid)

通过typelib,dll文件来访问COM组件

如果我们不知道COM组件的clsid或progid,但知道dll文件的路径,我们可以使用oleview.exe来查看clsid,或者可以通过comtypes.client中的GetModule函数来生成python binding,然后从python binding中查找clsid和iid:

module = GetModule(typelib/dll/exe)
dir(module)

构建自己的c/c++扩展

一个最简单的c扩展,来自python源代码PC/example_nt/example.c:

#include "Python.h"

static PyObject *
ex_foo(PyObject *self, PyObject *args)
{
    printf("Hello, world\n");
    Py_INCREF(Py_None);
    return Py_None;
}

static PyMethodDef example_methods[] = {
    {"foo", ex_foo, METH_VARARGS, "foo() doc string"},
    {NULL, NULL}
};

PyMODINIT_FUNC
initexample(void)
{
    Py_InitModule("example", example_methods);
}

在python中调用此模块的代码如下:

>>> import example
>>> example
<module 'example' from 'example.pyd'>
>>> example.foo()
Hello, world
>>> help(example.foo)
Help on built-in function foo in module example:

foo(...)
    foo() doc string

从中可以看出,一个基本的c扩展包含以下部分:

  1. 最开始 #include "Python.h"
  2. 模块方法实现
  3. 模块接口定义
  4. 模块初始化函数

更多内容见 Extending Python with C or C++ [4]

注解

需要实现更复杂的c扩展,可以利用python源代码中Modules/xxmodule.c作为模板

注解

c++扩展中会被python调用的方法,应该用 extern "C" 声明

可以使用 Boost.Python 库来简化c++扩展

distutils 编译c/c++扩展

当完成c/c++扩展的编写,为了能让python能够顺利访问扩展中的成员,我们还需要将c/c++源码编译成动态库.so/.pyd [3]

同样看一个简单的sample,来自python源代码PC/example_nt/setup.py:

from distutils.core import setup, Extension

example_mod = Extension('example', sources = ['example.c'])

setup(name = "example",
    version = "1.0",
    description = "A sample extension module",
    ext_modules = [example_mod],
)

扩展的编译是通过distutils中的Extension类来完成的,详见 [10]

注解

如果在windows上编译遇到 error: Unable to find vcvarsall.bat

则需要设置环境变量,将 VS90COMNTOOLS 设置为VS的Tools路径

比如, 如果你安装的是2013版 SET VS90COMNTOOLS=%VS120COMNTOOLS%

向c/c++程序里面内嵌python代码

示例代码:

#include <Python.h>

int
main(int argc, char *argv[])
{
  Py_SetProgramName(argv[0]);  /* optional but recommended */
  Py_Initialize();
  PyRun_SimpleString("from time import time,ctime\n"
                     "print 'Today is',ctime(time())\n");
  Py_Finalize();
  return 0;
}

简单内嵌步奏:

  1. 将程序名传入python解析器,使用函数Py_SetProgramName()
  2. 初始化python解析器,使用函数Py_Initialize()
  3. 执行python代码或文件,使用函数PyRun_SimpleString(),PyRun_SimpleFile()
  4. 关闭python解析器

用reStructuredText编写文档

Page Status:Development
Last Reviewed:

reStructuredText是一个纯文本的标记语言,可被用于python内嵌文档编写,网页编写或者用来写文章。

Python库docutils实现了对reStructuredText标记语言的解析支持,并能将其转化成其他常用的文档格式,如HTML,Latex,PDF

注解

建议学习reStructuredText语法的同时不断练习以加深印象。 在线演示网址网址的搭建过程

reStructuredText标记

内嵌标记

内嵌标记 能够使一段文字中的某些字段呈现出不同的特征

起始标记和结束标记相同的:

  • 强调: “*”
  • 加粗: “**”
  • 解析文本: “`”
  • 内嵌块: “``”
  • 替换: “|”

起始标记和结束标记不相同的:

  • 链接目标: “_`”, “`”
  • 脚注: “[”, “]_”
  • 链接: “`”, “`_” 或 “_”

Explicit标记

Explicit标记 由两个点号和一个空格组成,用于实现脚注,链接,注释和指令语法。

..

reStructuredText标记内嵌注意

当在文本中使用reStructuredText的标记和对单独词组使用reStructuredText的标记有所不同,需要有一些特别的注意点。见 reStructuredText内嵌语法

  1. 起始reStructuredText标记之前必须是:
  • 空格
  • ASCII字符 - : / ‘ ” < ( [ {
  • a non-ASCII punctuation character with Unicode category Pd (Dash), Po (Other), Pe (Close), Pf (Final quote), or Pi (Initial quote)
  1. 起始reStructuredText标记之后必须是一个非空格字符
  2. 结尾reStructuredText标记之后必须是:
  • 空格
  • ASCII字符 - : / ‘ ” < ( [ {
  • a non-ASCII punctuation character with Unicode category Pd (Dash), Po (Other), Pe (Close), Pf (Final quote), or Pi (Initial quote)
  1. 结尾reStructuredText标记之前必须是一个非空格字符
  2. 如果起始起始reStructuredText标记之前有ASCII字符 ‘ ” < ( [ {或者a character with Unicode character category Ps, Pi, or Pf则之后不能直接接ASCII字符’ ” ) ] } > or the categories Pe, Pf, or Pi
  3. 起始reStructuredText标记和结尾reStructuredText标记之间至少有一个字符
  4. 反斜杠字符会取消reStructuredText标记的语义

reStructuredText常见语法

链接相关语法

语法 输出 说明
`python <http://www.python.org>`_ python 内嵌外部链接
pypi_
.. _pypi: https://pypi.python.org/pypi
pypi 外部链接1
`python docs`_
.. _`python docs`: http://docs.python.org
python docs 外部链接2
`python intro`_
.. _`python intro`:
python introducation

python intro

python introducation

内部链接
`anonymous`__
__ http://www.python.org/
anonymous 匿名链接
[1]_
.. [1] this is a footnote

[1]

[1] this is a foot note
脚注
[citation]_
.. [citation] this is a citation

[citation]

[citation] this is a citation
引用

段落相关语法

普通段落
语法 输出 说明
This is a paragraph.

Paragraphs line up at
their left edges,
and are normally separated
by blank lines.
This is a paragraph.
Paragraphs line up at their left edges, and are normally separated by blank lines.
段落以空行分隔
标题
语法 输出 说明
parts
###########

chapters
***********

sections
=========

subsections
------------

subsubsections
^^^^^^^^^^^^^^^

paragraphs
"""""""""""""

parts

chapters

sections

subsections

subsubsections
paragraphs

标题由底部(或底部和顶部)连续的一组ASCII非字母数字的字符标识, 标题级别自动分配,最先出现的标题级别较高, 推荐使用标识字符有"= - ` : ' " ~ ^ _ * + # < >"。

Sphinx推荐在python文档中使用如下的规则:

  • # with overline, for parts
  • * with overline, for chapters
  • =, for sections
  • -, for subsections
  • ^, for subsubsections
  • ", for paragraphs
  • 列表

    列表的开始和结束各需要一个空行,列表中间的空行是可有可无的

    语法 输出 说明
    - This is item 1
    - This is item 2
    • This is item 1
    • This is item 2
    Bullet Lists
    3. This is the first item
    4. This is the second item
    5. Enumerators are arabic numbers, single letters, or roman numerals
    6. List items should be sequentially numbered, but need not start at 1 (although not all formatters will honour the first index).
    #. This item is auto-enumerated
    1. This is the first item
    2. This is the second item
    3. Enumerators are arabic numbers, single letters, or roman numerals
    4. List items should be sequentially numbered, but need not start at 1 (although not all formatters will honour the first index).
    5. This item is auto-enumerated
    Enumerated Lists
    what
    Definition lists associate a term with a definition.
    how
    The term is a one-line phrase, and the definition is one or more paragraphs or body elements, indented relative to the term. Blank lines are not allowed between term and definition.
    what
    Definition lists associate a term with a definition.
    how
    The term is a one-line phrase, and the definition is one or more paragraphs or body elements, indented relative to the term. Blank lines are not allowed between term and definition.
    Definition Lists
    :Authors:
    Tony J. (Tibs) Ibbs,
    David Goodger

    (and sundry other good-natured folks)

    :Version: 1.0 of 2001/08/08
    :Dedication: To my father.
    Authors: Tony J. (Tibs) Ibbs, David Goodger
    (and sundry other good-natured folks)
    Version: 1.0 of 2001/08/08
    Dedication: To my father.
    Field Lists
    -a command-line option "a"
    -b file options can have arguments
    and long descriptions
    --long options can be long also
    --input=file long options can also have
    arguments
    /V DOS/VMS-style options too
    -a command-line option "a"
    -b file options can have arguments and long descriptions
    --long options can be long also
    --input=file long options can also have arguments
    /V DOS/VMS-style options too
    Option Lists
    文字块

    注解

    文字块中的特殊字符不会被解析和替代, 所有的特殊字符,空格和换行符会被保留。

    语法 输出 说明
    ``内嵌文字块``
    内嵌文字块
    内嵌块经常用于显示一段短小的代码
    只有双引号``::``的段落表明接下来的所有缩进的/引用的文字都是一个文字块

    ::

        所有的reStructureText的转义字符如空格,换行,空行
        (like *this* or \this) 都不会被转义,会被直接保留。

        结果中不会保留双引号

    双引号``::``可以位于一个段落的最后,
    如果双引号``::``后面接空格,双引号会被忽略,
    如果双引号``::``后面接文字,双引号会变成一个单引号,
    比如::

        这样很方便

    当缩进恢复正常,文字段结束,
    所以我们可以在文字段中使用不同的缩进::

             8个空格的缩进
         4个空格的缩进
       2个空格的缩进

    也使用引用来标识文字块,合法的引用符号有::

      ! " # $ % & ' ( ) * + , - . / : ;
      < = > ? @ [ \ ] ^ _ ` { | } ~

    利用引用标识的文字块示例::

    > 引用的文字块第一行
    > 引用的文字块第二行

    只有双引号``::``的段落表明接下来的所有缩进的/引用的文字都是一个文字块

       所有的reStructureText的转义字符如空格,换行,空行
       (like *this* or \this) 都不会被转义,会被直接保留
    
       结果中不会保留双引号

    双引号``::``可以位于一个段落的最后 如果双引号``::``后面接空格,双引号会被忽略 如果双引号``::``后面接文字,双引号会变成一个单引号 比如:

       这样很方便

    当缩进恢复正常,文字段结束 所以我们可以在文字段中使用不同的缩进:

            8个空格的缩进
        4个空格的缩进
      2个空格的缩进

    也使用引用来标识文字块,合法的引用符号有:

    ! " # $ % & ' ( ) * + , - . / : ;
    < = > ? @ [ \ ] ^ _ ` { | } ~

    利用引用标识的文字块示例:

    > 引用的文字块第一行
    > 引用的文字块第二行
    段落块
    两个冒号加一个空行后面所有的缩进的段落都是块
    | Line blocks are useful for addresses,
    | verse, and adornment-free lists.
    |
    | Each new line begins with a
    | vertical bar ("|").
    | Line breaks and initial indents
    | are preserved.
    | Continuation lines are wrapped
    portions of long lines; they begin
    with spaces in place of vertical bars.
    Line blocks are useful for addresses,
    verse, and adornment-free lists.

    Each new line begins with a
    vertical bar ("|").
    Line breaks and initial indents
    are preserved.
    Continuation lines are wrapped
    portions of long lines;
    they begin with spaces in place
    of vertical bars.
    行块
    注释

    没有有效标记(如脚注)的直解标记(.. )文本块就是注释(参考) 例如:

    .. This is a comment.

    可以用缩进文本来进行多行注释:

    ..
      This whole indented block
      is a comment.
    
      Still in the comment.
    

    注解

    注释内容在输出中可能不可见

    代码块

    可以用如3.2.4节文字块的方法来标识代码块:

    示例

    python code::
    
     #python code
     def one_function():
         pass
    

    输出

    python code:

    #python code
    def one_function():
        pass
    

    表格语法

    reStructureText表格

    注解

    没有好的编辑器支持的话,建议不要使用reStructureText的表格,写起来很费时间。

    指令语法

    指令是reStructuredText用来在不改变/新增已有语法的基础上,扩展新的特性的一种机制。

    reStructuredText标准指令文档 罗列了所有的标准指令

    其他的指令由各自的解析器自己定义,比如sphinx就支持很多 自定义的指令

    指令语法示意:

    +-------+------------------+
    | ".. " | 指令类型 "::" 指令 |
    +-------+                |
            |                  |
            +------------------+
    

    指令块由指令符后面所有缩进内容组成,指令块可以包含三部分:

    1. Directive arguments
    2. Directive options
    3. Directive content

    Directive arguments和Directive options紧接着指令。Directive content和它们之间用空行分隔。

    不同的指令对指令块的要求不同,如果提供的指令块不符合要求,会导致错误

    下面介绍一些常用的标准指令和sphinx扩充的指令。

    reStructuredText标准指令
    提醒指令

    提醒指令 ,包含”attention”, “caution”, “danger”, “error”, “hint”, “important”, “note”, “tip”, “warning”, “admonition”

    示例:

    .. attention::
     Attention Please!
    

    输出

    注意

    Attention Please!

    image指令

    图片指令 向输出中插入指定图片

    示例:

    .. image:: images/happy_dog.jpg
      :heigsht: 200px
      :width: 300 px
      :scale: 50 %
      :alt: 快乐的狗狗
    

    输出

    快乐的狗狗
    role指令

    role指令 建立并向解析器注册了role类型,所有mark成这种role类型的文本都会被解析器以这种类型解析。

    reStructureText定义了一些 标准role类型

    sphinx也扩充了 一些role类型

    自定义role类型示例

    使用role指令动态定义一种文本解析方式:

    .. role:: custom
    

    文本中使用custom类型的文本会被按照custom类型来解析:

    An example of using :custom:`interpreted text`
    

    结果会类似于这样:

    <paragraph>
       An example of using
       <inline classes="custom">
           interpreted text
    

    注解

    role指令必须先定义,再使用

    基于已有role类型的示例:

    可以基于已有的role类型来定义新的role类型,新的role类型将是已有类型的一个子集

    定义:

    .. role:: raw-role(raw)
       :format: html
    

    使用:

    用html实现 :raw-role:`<strong>加粗的文本</strong>`
    

    输出:

    用html实现 加粗的文本

    sphinx扩充的指令

    sphinx对reStructureText的指令有所扩充,见 Sphinx Markup Constructs

    toctree 指令

    toctree指令 用来关联各个独立的文档, 将他们组织成一个整体来方便索引。

    注解

    reStructtureText没有语法来组织独立的文档

    用sphinx组织文档

    Page Status:Development
    Last Reviewed:

    sphinx是一个文本builder,能将一组reStructureText文档转换成其他格式(HTML, LaTex),能自动解析处理reStructureText标记。

    安装sphinx

    建议通过easy_install或pip来安装sphinx,能够自动处理依赖,简单迅速。

    使用sphinx

    sphinx提供了工具来简化了文档工程的配置和生成,使得用户能够更加专注于文档的内容。

    对于一般的文档工程,使用sphinx通常包含有几个步奏, 见 sphinx初尝

    1. 使用工具sphinx-quickstart快速配置文档资源
    2. 修改index.rst来定义文档结构
    3. 使用reStructureText添加文档内容
    4. build,输出结果

    sphinx的更多内容

    上面4个步奏已经可以满足大部分一般用户的需求,如果用户需要使用sphinx的更多功能(更多标记,更多扩展,定制模板),则需要对sphinx有更多的了解。

    sphinx域

    sphinx域是为某些语言输出提供特定的语法, 内置的支持包含python,c,c++,javascript,reStructureText。见 sphinx domain

    比如我想输出一个python的函数

    想要得到的效果:

    format_exception(etype, value, tb[, limit=None])
     Format the exception with a traceback.
    
     Parameters: •etype – exception type
                 •value – exception value
                 •tb – traceback object
                 •limit (integer or None) – maximum number of stack frames to show
    
     Return type: list of strings
    

    在文档中的写法:

    .. py:function:: format_exception(etype, value, tb[, limit=None])
    
      Format the exception with a traceback.
    
      :param etype: exception type
      :param value: exception value
      :param tb: traceback object
      :param limit: maximum number of stack frames to show
      :type limit: integer or None
      :rtype: list of strings
    

    模板

    sphinx使用 Jinja 作为HTML的模板语言

    扩展

    Autodoc

    sphinx的一些技巧

    python debug

    Page Status:Development
    Last Reviewed:

    pdb

    python自带的调试器,使用 python -m pdb myscript.py 或者 import pdb; pdb.set_trace() 进入调试模式(post-mortem debugging)

    debug python code with gdb

    pdb可以解决大部分python调试的需求,但是在一些复杂场景下,需要更强大的工具.
    • 调试正在运行的process
    • 调试可能hng的程序
    • 调试多线程

    参考:

    https://wiki.python.org/moin/DebuggingWithGdb
    

    remote debug

    remote debug一般是IDE中提供的功能, 对于pycharm, 参考:

    https://www.jetbrains.com/help/pycharm/2017.1/remote-debugging.html#6
    

    Indices and tables