15.1. 交互模式

最后更新于:2022-04-01 00:50:24

### 15.1.1\. 错误处理 当错误发生时,解释器打印一个错误信息和堆栈跟踪。在交互模式下,它返回主提示符;当输入来自文件的时候,在打印堆栈跟踪后以非零退出状态退出。(在 try 声明中被 except 子句捕捉到的异常在这种情况下不是错误。)有些错误是非常致命的会导致一个非零状态的退出;这也适用于内部错误以及某些情况的内存耗尽。所有的错误信息都写入到标准错误流;来自执行的命令的普通输出写入到标准输出。 输入中断符(通常是 Control-C 或者 DEL)到主或者从提示符中慧取消输入并且返回到主提示。[[1]](http://www.pythondoc.com/pythontutorial3/appendix.html#id7)当命令执行中输入中断符会引起 KeyboardInterrupt 异常,这个异常能够被一个 try 声明处理。 ### 15.1.2\. 可执行 Python 脚本 在 BSD’ish Unix 系统上,Python 脚本可直接执行,像 shell 脚本一样,只需要把下面内容加入到 ~~~ #!/usr/bin/env python3.4 ~~~ (假设 python 解释器在用户的 PATH 中)脚本的开头,并给予该文件的可执行模式。#! 必须是文件的头两个字符。在一些系统上,第一行必须以 Unix-style 的行结束符('\n')结束,不能以 Windows 的行结束符('\r\n')。 注意 '#' 在 Python 中是用于注释的。 使用 **chmod** 命令能够给予脚本执行模式或者权限。 ~~~ $ chmod +x myscript.py ~~~ 在 Windows 系统上,没有一个 “可执行模式” 的概念。Python 安装器会自动地把 .py 文件和python.exe 关联起来,因此双击 Python 分拣将会把它当成一个脚本运行。文件扩展名也可以是.pyw,在这种情况下,运行时不会出现控制台窗口。 ### 15.1.3\. 交互式启动文件 当你使用交互式 Python 的时候,它常常很方便地执行一些命令在每次解释器启动时。你可以这样做:设置一个名为 PYTHONSTARTUP 的环境变量为包含你的启动命令的文件名。这跟 Unix shells 的.profile 特点有些类似。 这个文件在交互式会话中是只读的,在当 Python 从脚本中读取命令,以及在当 /dev/tty 被作为明确的命令源的时候不只是可读的。该文件在交互式命令被执行的时候在相同的命名空间中能够被执行,因此在交互式会话中定义或者导入的对象能够无需授权就能使用。你也能在文件中更改提示sys.ps1 和 sys.ps2。 如果你想要从当前目录中读取一个附加的启动文件,你可以在全局启动文件中编写代码像这样:if os.path.isfile('.pythonrc.py'): exec(open('.pythonrc.py').read())。如果你想要在脚本中使用启动文件的话,你必须在脚本中明确要这么做: ~~~ import os filename = os.environ.get('PYTHONSTARTUP') if filename and os.path.isfile(filename): with open(filename) as fobj: startup_file = fobj.read() exec(startup_file) ~~~ ### 15.1.4\. 定制模块 Python 提供两个钩子为了让你们定制 sitecustomize 和 usercustomize。为了看看它的工作机制的话,你必须首先找到你的用户 site-packages 目录的位置。启动 Python 并且运行这段代码: ~~~ >>> import site >>> site.getusersitepackages() '/home/user/.local/lib/python3.4/site-packages' ~~~ 现在你可以创建一个名为 usercustomize.py 的文件在你的用户 site-packages 目录,并且在里面放置你想要的任何内容。它会影响 Python 的每一次调用,除非它以 _-s_ (禁用自动导入)选项启动。 sitecustomize 以同样地方式工作,但是通常由是机器的管理员创建在全局的 site-packages 目录中,并且是在 usercustomize 之前导入。请参阅 site 模块获取更多信息。 Footnotes | [[1]](http://www.pythondoc.com/pythontutorial3/appendix.html#id4) | GNU 的 Readline 包的问题可能会阻止这种做法。 |
';

15. 附录

最后更新于:2022-04-01 00:50:22

';

14.1. 表达错误

最后更新于:2022-04-01 00:50:20

这一节详细说明 “0.1” 示例,教你怎样自己去精确的分析此类案例。假设这里你已经对浮点数表示有基本的了解。 _Representation error_ 提及事实上有些(实际是大多数)十进制小数不能精确的表示为二进制小数。这是 Python (或 Perl,C,C++,Java,Fortran 以及其它很多)语言往往不能按你期待的样子显示十进制数值的根本原因: ~~~ >>> 0.1 + 0.2 0.30000000000000004 ~~~ 这是为什么? 1/10 不能精确的表示为二进制小数。大多数今天的机器(2000年十一月)使用 IEEE-754 浮点数算法,大多数平台上 Python 将浮点数映射为 IEEE-754 “双精度浮点数”。754 双精度包含 53 位精度,所以计算机努力将输入的 0.1 转为 J/2**N 最接近的二进制小数。_J_ 是一个 53 位的整数。改写: 1 / 10 ~= J / (2**N) 为: J ~= 2**N / 10 J 重现时正是 53 位(是 >= 2**52 而非 < 2**53 ), N 的最佳值是 56: ~~~ >>> 2**52 4503599627370496 >>> 2**53 9007199254740992 >>> 2**56/10 7205759403792793 ~~~ 因此,56 是保持 J 精度的唯一 N 值。J 最好的近似值是整除的商: ~~~ >>> q, r = divmod(2**56, 10) >>> r 6 ~~~ 因为余数大于 10 的一半,最好的近似是取上界: ~~~ >>> q+1 7205759403792794 ~~~ 因此在 754 双精度中 1/10 最好的近似值是是 2**56,或: 7205759403792794 / 72057594037927936 要注意因为我们向上舍入,它其实比 1/10 稍大一点点。如果我们没有向上舍入,它会比 1/10 稍小一点。但是没办法让它 恰好 是 1/10! 所以计算机永远也不 “知道” 1/10:它遇到上面这个小数,给出它所能得到的最佳的 754 双精度实数: ~~~ >>> .1 * 2**55 7205759403792794.0 ~~~ 如果我们把这小数乘以 10**55,我们可以看到其55位十进制数的值: ~~~ >>> 3602879701896397 * 10 ** 55 // 2 ** 55 1000000000000000055511151231257827021181583404541015625 ~~~ 这表示存储在计算机中的实际值近似等于十进制值 0.1000000000000000055511151231257827021181583404541015625。许多语言(包括旧版本的Python)会把结果舍入到17位有效数字,而不是显示全部的十进制值: ~~~ >>> format(0.1, '.17f') '0.10000000000000001' ~~~ fractions 和 decimal 模块使得这些计算很简单: ~~~ >>> from decimal import Decimal >>> from fractions import Fraction >>> Fraction.from_float(0.1) Fraction(3602879701896397, 36028797018963968) >>> (0.1).as_integer_ratio() (3602879701896397, 36028797018963968) >>> Decimal.from_float(0.1) Decimal('0.1000000000000000055511151231257827021181583404541015625') >>> format(Decimal.from_float(0.1), '.17') '0.10000000000000001' ~~~
';

14. 浮点数算法:争议和限制

最后更新于:2022-04-01 00:50:17

浮点数在计算机中表达为二进制(binary)小数。例如:十进制小数: 0.125 是 1/10 + 2/100 + 5/1000 的值,同样二进制小数: 0.001 是 0/2 + 0/4 + 1/8。这两个数值相同。唯一的实质区别是第一个写为十进制小数记法,第二个是二进制。 遗憾的是,大多数十进制小数不能精确的表达二进制小数。 这个问题更早的时候首先在十进制中发现。考虑小数形式的 1/3 ,你可以来个十进制的近似值。 0.3 或者更进一步的, 0.33 或者更进一步的, 0.333 诸如此类。如果你写多少位,这个结果永远不是精确的 1/3 ,但是可以无限接近 1/3 。 同样,无论在二进制中写多少位,十进制数 0.1 都不能精确表达为二进制小数。二进制来表达 1/10 是一个无限循环小数: 0.0001100110011001100110011001100110011001100110011... 在任意无限位数值中中止,你可以得到一个近似。 在一个典型的机器上运行 Python,一共有53位的精度来表示一个浮点数,所以当你输入十进制的0.1 的时候,看到是一个二进制的小数: 0.00011001100110011001100110011001100110011001100110011010 非常接近,但是不完全等于,1/10。 这是很容易忘记,存储的值是一个近似的原小数,由于浮体的方式,显示在提示符的解释。Python 中只打印一个小数近似的真实机器所存储的二进制近似的十进制值。如果 Python 要打印存储的二进制近似真实的十进制值 0.1,那就要显示: ~~~ >>> 0.1 0.1000000000000000055511151231257827021181583404541015625 ~~~ 这么多位的数字对大多数人是没有用的,所以 Python 显示一个舍入的值 ~~~ >>> 1 / 10 0.1 ~~~ 只要记住即使打印的结果看上去是精确的 1/10,真正存储的值是最近似的二进制小数。 有趣地是,存在许多不同的十进制数共享着相同的近似二进制小数。例如,数字 0.1 和0.10000000000000001 以及 0.1000000000000000055511151231257827021181583404541015625 都是3602879701896397 / 2 ** 55 的近似值。因为所有这些十进制数共享相同的近似值,在保持恒等式eval(repr(x)) == x 的同时,显示的可能是它们中的任何一个。 历史上,Python 提示符和内置的 repr() 函数选择一个 17 位精度的数字,0.10000000000000001。从 Python 3.1 开始,Python(在大多数系统上)能够从这些数字当中选择最短的一个并简单地显示0.1。 注意,这是二进制浮点数的自然性质:它不是 Python 中的一个 bug,也不是你的代码中的 bug。你会看到所有支持硬件浮点数算法的语言都会有这个现象(尽管有些语言默认情况下或者在所有输出模式下可能不会 _显示_ 出差异)。 为了输出更好看,你可能想用字符串格式化来生成固定位数的有效数字: ~~~ >>> format(math.pi, '.12g') # give 12 significant digits '3.14159265359' >>> format(math.pi, '.2f') # give 2 digits after the point '3.14' >>> repr(math.pi) '3.141592653589793' ~~~ 认识到这,在真正意义上,是一种错觉是很重要的:你在简单地舍入真实机器值的 _显示_。 例如,既然 0.1 不是精确的 1/10,3 个 0.1 的值相加可能也不会得到精确的 0.3: ~~~ >>> .1 + .1 + .1 == .3 False ~~~ 另外,既然 0.1 不能更接近 1/10 的精确值而且 0.3 不能更接近 3/10 的精确值,使用 round() 函数提前舍入也没有帮助: ~~~ >>> round(.1, 1) + round(.1, 1) + round(.1, 1) == round(.3, 1) False ~~~ 虽然这些数字不可能再更接近它们想要的精确值,round() 函数可以用于在计算之后进行舍入,这样的话不精确的结果就可以和另外一个相比较了: ~~~ >>> round(.1 + .1 + .1, 10) == round(.3, 10) True ~~~ 二进制浮点数计算有很多这样意想不到的结果。“0.1”的问题在下面”误差的表示”一节中有准确详细的解释。更完整的常见怪异现象请参见 [浮点数的危险](http://www.lahey.com/float.htm)。 最后我要说,“没有简单的答案”。也不要过分小心浮点数!Python 浮点数计算中的误差源之于浮点数硬件,大多数机器上每次计算误差不超过 2**53 分之一。对于大多数任务这已经足够了,但是你要在心中记住这不是十进制算法,每个浮点数计算可能会带来一个新的舍入错误。 虽然确实有问题存在,对于大多数平常的浮点数运算,你只要简单地将最终显示的结果舍入到你期望的十进制位数,你就会得到你期望的最终结果。str() 通常已经足够用了,对于更好的控制可以参阅 [格式化字符串语法](http://python.usyiyi.cn/python_341/library/string.html#formatstrings) 中 str.format() 方法的格式说明符。 对于需要精确十进制表示的情况,可以尝试使用 decimal 模块,它实现的十进制运算适合会计方面的应用和高精度要求的应用。 fractions 模块支持另外一种形式的运算,它实现的运算基于有理数(因此像1/3这样的数字可以精确地表示)。 如果你是浮点数操作的重度使用者,你应该看一下由 SciPy 项目提供的 Numerical Python 包和其它用于数学和统计学的包。参看 。 当你真的 _真_ 想要知道浮点数精确值的时候,Python 提供这样的工具可以帮助你。float.as_integer_ratio() 方法以分数的形式表示一个浮点数的值: ~~~ >>> x = 3.14159 >>> x.as_integer_ratio() (3537115888337719, 1125899906842624) ~~~ 因为比值是精确的,它可以用来无损地重新生成初始值: ~~~ >>> x == 3537115888337719 / 1125899906842624 True ~~~ float.hex() 方法以十六进制表示浮点数,给出的同样是计算机存储的精确值: ~~~ >>> x.hex() '0x1.921f9f01b866ep+1' ~~~ 精确的十六进制表示可以用来准确地重新构建浮点数: ~~~ >>> x == float.fromhex('0x1.921f9f01b866ep+1') True ~~~ 因为可以精确表示,所以可以用在不同版本的 Python(与平台相关)之间可靠地移植数据以及与支持同样格式的其它语言(例如 Java 和 C99)交换数据。 另外一个有用的工具是 math.fsum() 函数,它帮助求和过程中减少精度的损失。当数值在不停地相加的时候,它会跟踪“丢弃的数字”。这可以给总体的准确度带来不同,以至于错误不会累积到影响最终结果的点: ~~~ >>> sum([0.1] * 10) == 1.0 False >>> math.fsum([0.1] * 10) == 1.0 True ~~~
';

13.4. 其它交互式解释器

最后更新于:2022-04-01 00:50:15

跟早先版本的解释器比,现在已经有了很大的进步。不过,还是有些期待没有完成:它应该在后继行中优美的提供缩进(解释器知道下一行是否需要缩进)建议。完成机制可以使用解释器的符号表。命名检查(或进一步建议)匹配括号、引号等等。 另有一个强化交互式解释器已经存在一段时间了,它就是 [IPython](http://ipython.scipy.org/),它支持 tab 完成,对象浏览和高级历史管理。它也可以完全定制或嵌入到其它应用程序中。另一个类似的强化交互环境是 [bpython](http://www.bpython-interpreter.org/) 。
';

13.3. 快捷键绑定

最后更新于:2022-04-01 00:50:13

Readline 库的快捷键绑定和其它一些参数可以通过名为 ~/.inputrc 的初始化文件的替换命名来定制。快捷键绑定如下形式: `key-name: function-name` 或者: `"string": function-name` 选项可以如下设置: `set option-name value` 例如: ~~~ # I prefer vi-style editing: set editing-mode vi # Edit using a single line: set horizontal-scroll-mode On # Rebind some keys: Meta-h: backward-kill-word "\C-u": universal-argument "\C-x\C-r": re-read-init-file ~~~ 需要注意的是 Python 中默认 Tab 绑定为插入一个 Tab 字符而不是 Readline 库的默认文件名完成函数,如果你想用这个,可以将以下内容插入: `Tab: complete` 到你的 ~/.inputrc 中来覆盖它。(当然,如果你真的把 Tab 设置成这样,就很难在后继行中插入缩进。) 自动完成变量和模块名也可以激活生效。要使之在解释器交互模式中可用,在你的启动文件中加入下面内容: [[1]](http://www.pythondoc.com/pythontutorial3/interactive.html#id7) ~~~ import rlcompleter, readline readline.parse_and_bind('tab: complete') ~~~ 这个操作将 Tab 绑定到完成函数,故按 Tab 键两次会给出建议的完成内容;它查找 Python 命名、当前的局部变量、有效的模块名。对于类似 string.a 这样的文件名,它会解析 '.' 相关的表达式,从返回的结果对象中获取属性,以提供完成建议。需要注意的是,如果对象的__getattr__() 方法是此表达式的一部分,这可能会执行应用程序定义代码。 更有用的初始化文件可能是下面这个例子这样的。要注意一旦创建的名字没用了,它会删掉它们;因为初始化文件作为解释命令与之在同一个命名空间执行,在交互环境中删除命名带来了边际效应。可能你发现了它体贴的保留了一些导入模块,类似 os ,在解释器的大多数使用场合中都会用到它们。 ~~~ # Add auto-completion and a stored history file of commands to your Python # interactive interpreter. Requires Python 2.0+, readline. Autocomplete is # bound to the Esc key by default (you can change it - see readline docs). # # Store the file in ~/.pystartup, and set an environment variable to point # to it: "export PYTHONSTARTUP=~/.pystartup" in bash. import atexit import os import readline import rlcompleter historyPath = os.path.expanduser("~/.pyhistory") def save_history(historyPath=historyPath): import readline readline.write_history_file(historyPath) if os.path.exists(historyPath): readline.read_history_file(historyPath) atexit.register(save_history) del os, atexit, readline, rlcompleter, save_history, historyPath ~~~
';

13.2. 历史回溯

最后更新于:2022-04-01 00:50:11

历史代替可以工作。所有非空的输入行都被保存在历史缓存中,获得一个新的提示符的时候,你处于这个缓存的最底的空行。 C-P 在历史缓存中上溯一行, C-N 向下移一行。历史缓存中的任一行都可以编辑;按下 Return 键时将当前行传入解释器。 C-R 开始一个增量向前搜索;C-S 开始一个向后搜索。
';

13.1. 行编辑

最后更新于:2022-04-01 00:50:08

如果支持,无论解释器打印主提示符或从属提示符,行编辑都会激活。当前行可以用 Emacs 风格的快捷键编辑。其中最重要的是: C-A (Control-A)将光标移动到行首, C-E 移动到行尾, C-B向左移一个字符, C-F 向右移一位。退格向左删除一个符串, C-D 向右删除一个字符。 C-K 删掉光标右边直到行尾的所有字符, C-Y 将最后一次删除的字符串粘贴到光标位置。 C-underscore(underscores 即下划线,译注)撤销最后一次修改,它可以因积累作用重复。
';

13. 交互式输入行编辑历史回溯

最后更新于:2022-04-01 00:50:06

有些版本的 Python 解释器支持输入行编辑和历史回溯,类似 Korn shell 和 GNU bash shell 的功能。这是通过 [GNU Readline](http://tiswww.case.edu/php/chet/readline/rltop.html) 库实现的。它支持 Emacs 风格和 vi 风格的编辑。这个库有它自己的文档,在此不重复了。不过,基本的东西很容易演示。交互式编辑和历史查阅在 Unix 和 Cygwin 版中是可选项。 本章 _不是_ 马克 哈密尔顿的 PythonWin 包和随 Python 发布的基于 TK 的 IDLE 环境的文档。 NT 系统和其它 DOS、Windows 系统上的 DOS 窗中的命令行历史回调,属于另一个话题。
';

12. 接下来?

最后更新于:2022-04-01 00:50:04

读过这本指南应该会让你有兴趣使用 Python —— 可能你已经期待着用 Python 解决你的实际问题了。可以在哪里进行一步学习? 入门指南是 Python 文档集的一部分。其中的另一些文档包括: * _library-index_: 应该浏览一下这份文档,它为标准库中的类型、函数和模块提供了完整(尽管很简略)的参考资料。标准的 Python 发布版包括了 大量 的附加模块。其中有针对读取 Unix 邮箱、接收 HTTP 文档、生成随机数、解析命令行选项、写 CGI 程序、压缩数据以及很多其它任务的模块。略读一下库参考会给你很多解决问题的思路。 * _install-index_ 展示了如何安装其他 Python 用户编写的附加模块。 * _reference-index_: 詳細说明了 Python 语法和语义。它读起来很累,不过对于语言本身,有份完整的手册很有用。 其它 Python 资源: * [http://www.python.org](http://www.python.org/): Python 官方网站。它包含代码、文档和 Web 上与 Python 有关的页面链接该网站镜像于全世界的几处其它问题,类似欧洲、日本和澳大利亚。镜像可能会比主站快,这取决于你的地理位置。 * [http://docs.python.org](http://docs.python.org/): 快速访问 Python 的文档。 * [http://pypi.python.org](http://pypi.python.org/): Python 包索引,以前昵称为奶酪店,索引了可供下载的,用户创建的 Python 模块。如果你发布了代码,可以注册到这里,这样别人可以找到它。 * [http://aspn.activestate.com/ASPN/Python/Cookbook/](http://aspn.activestate.com/ASPN/Python/Cookbook/): Python 食谱是大量的示例代码、大型的集合,和有用的脚本。值得关注的是这次资源已经结集成书,名为《Python 食谱》(O’Reilly & Associates, ISBN 0-596-00797-3。) 与 Python 有关的问题,以及问题报告,可以发到新闻组 _comp.lang.python_ ,或者发送到邮件组[python-list@python.org](mailto:python-list%40python.org) 。新闻组和邮件组是开放的,所以发送的消息可以自动的跟到另一个之后。每天有超过 120 个投递(高峰时有数百),提问(以及回答)问题,为新功能提建议,发布新模块。在发信之前,请查阅 [常见问题](http://www.python.org/doc/faq/) (亦称 FAQ),或者在 Python 源码发布包的 Misc/ 目录中查阅。邮件组也可以在 [http://mail.python.org/pipermail/](http://mail.python.org/pipermail/) 访问。FAQ回答了很多被反复提到的问题,很可能已经解答了你的问题。
';

11.8. 十进制浮点数算法

最后更新于:2022-04-01 00:50:01

decimal 模块提供了一个 Decimal 数据类型用于浮点数计算。相比内置的二进制浮点数实现float,这个类型有助于 * 金融应用和其它需要精确十进制表达的场合, * 控制精度, * 控制舍入以适应法律或者规定要求, * 确保十进制数位精度,或者 * 用户希望计算结果与手算相符的场合。 例如,计算 70 分电话费的 5% 税计算,十进制浮点数和二进制浮点数计算结果的差别如下。如果在分值上舍入,这个差别就很重要了: ~~~ >>> from decimal import * >>> round(Decimal('0.70') * Decimal('1.05'), 2) Decimal('0.74') >>> round(.70 * 1.05, 2) 0.73 ~~~ Decimal 的结果总是保有结尾的 0,自动从两位精度延伸到4位。Decimal 重现了手工的数学运算,这就确保了二进制浮点数无法精确保有的数据精度。 高精度使 Decimal 可以执行二进制浮点数无法进行的模运算和等值测试: ~~~ >>> Decimal('1.00') % Decimal('.10') Decimal('0.00') >>> 1.00 % 0.10 0.09999999999999995 >>> sum([Decimal('0.1')]*10) == Decimal('1.0') True >>> sum([0.1]*10) == 1.0 False ~~~ decimal 提供了必须的高精度算法: ~~~ >>> getcontext().prec = 36 >>> Decimal(1) / Decimal(7) Decimal('0.142857142857142857142857142857142857') ~~~
';

11.7. 列表工具

最后更新于:2022-04-01 00:49:59

很多数据结构可能会用到内置列表类型。然而,有时可能需要不同性能代价的实现。 array 模块提供了一个类似列表的 array() 对象,它仅仅是存储数据,更为紧凑。以下的示例演示了一个存储双字节无符号整数的数组(类型编码 "H" )而非存储 16 字节 Python 整数对象的普通正规列表: ~~~ >>> from array import array >>> a = array('H', [4000, 10, 700, 22222]) >>> sum(a) 26932 >>> a[1:3] array('H', [10, 700]) ~~~ collections 模块提供了类似列表的 deque() 对象,它从左边添加(append)和弹出(pop)更快,但是在内部查询更慢。这些对象更适用于队列实现和广度优先的树搜索: ~~~ >>> from collections import deque >>> d = deque(["task1", "task2", "task3"]) >>> d.append("task4") >>> print("Handling", d.popleft()) Handling task1 unsearched = deque([starting_node]) def breadth_first_search(unsearched): node = unsearched.popleft() for m in gen_moves(node): if is_goal(m): return m unsearched.append(m) ~~~ 除了链表的替代实现,该库还提供了 bisect 这样的模块以操作存储链表: ~~~ >>> import bisect >>> scores = [(100, 'perl'), (200, 'tcl'), (400, 'lua'), (500, 'python')] >>> bisect.insort(scores, (300, 'ruby')) >>> scores [(100, 'perl'), (200, 'tcl'), (300, 'ruby'), (400, 'lua'), (500, 'python')] ~~~ heapq 提供了基于正规链表的堆实现。最小的值总是保持在 0 点。这在希望循环访问最小元素但是不想执行完整堆排序的时候非常有用: ~~~ >>> from heapq import heapify, heappop, heappush >>> data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] >>> heapify(data) # rearrange the list into heap order >>> heappush(data, -5) # add a new entry >>> [heappop(data) for i in range(3)] # fetch the three smallest entries [-5, 0, 1] ~~~
';

11.6. 弱引用

最后更新于:2022-04-01 00:49:57

Python 自动进行内存管理(对大多数的对象进行引用计数和垃圾回收—— _garbage collection_ ——以循环利用)在最后一个引用消失后,内存会很快释放。 这个工作方式对大多数应用程序工作良好,但是偶尔会需要跟踪对象来做一些事。不幸的是,仅仅为跟踪它们创建引用也会使其长期存在。 weakref 模块提供了不用创建引用的跟踪对象工具,一旦对象不再存在,它自动从弱引用表上删除并触发回调。典型的应用包括捕获难以构造的对象: ~~~ >>> import weakref, gc >>> class A: ... def __init__(self, value): ... self.value = value ... def __repr__(self): ... return str(self.value) ... >>> a = A(10) # create a reference >>> d = weakref.WeakValueDictionary() >>> d['primary'] = a # does not create a reference >>> d['primary'] # fetch the object if it is still alive 10 >>> del a # remove the one reference >>> gc.collect() # run garbage collection right away 0 >>> d['primary'] # entry was automatically removed Traceback (most recent call last): File "", line 1, in d['primary'] # entry was automatically removed File "C:/python33/lib/weakref.py", line 46, in __getitem__ o = self.data[key]() KeyError: 'primary' ~~~
';

11.5. 日志

最后更新于:2022-04-01 00:49:55

logging 模块提供了完整和灵活的日志系统。它最简单的用法是记录信息并发送到一个文件或sys.stderr: ~~~ import logging logging.debug('Debugging information') logging.info('Informational message') logging.warning('Warning:config file %s not found', 'server.conf') logging.error('Error occurred') logging.critical('Critical error -- shutting down') ~~~ 输出如下: ~~~ WARNING:root:Warning:config file server.conf not found ERROR:root:Error occurred CRITICAL:root:Critical error -- shutting down ~~~ 默认情况下捕获信息和调试消息并将输出发送到标准错误流。其它可选的路由信息方式通过 email,数据报文,socket 或者 HTTP Server。基于消息属性,新的过滤器可以选择不同的路由: DEBUG,INFO, WARNING, ERROR 和 CRITICAL 。 日志系统可以直接在 Python 代码中定制,也可以不经过应用程序直接在一个用户可编辑的配置文件中加载。
';

11.4. 多线程

最后更新于:2022-04-01 00:49:52

线程是一个分离无顺序依赖关系任务的技术。在某些任务运行于后台的时候应用程序会变得迟缓,线程可以提升其速度。一个有关的用途是在 I/O 的同时其它线程可以并行计算。 下面的代码显示了高级模块 threading 如何在主程序运行的同时运行任务: ~~~ import threading, zipfile class AsyncZip(threading.Thread): def __init__(self, infile, outfile): threading.Thread.__init__(self) self.infile = infile self.outfile = outfile def run(self): f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED) f.write(self.infile) f.close() print('Finished background zip of:', self.infile) background = AsyncZip('mydata.txt', 'myarchive.zip') background.start() print('The main program continues to run in foreground.') background.join() # Wait for the background task to finish print('Main program waited until background was done.') ~~~ 多线程应用程序的主要挑战是协调线程,诸如线程间共享数据或其它资源。为了达到那个目的,线程模块提供了许多同步化的原生支持,包括:锁,事件,条件变量和信号灯。 尽管这些工具很强大,微小的设计错误也可能造成难以挽回的故障。因此,任务协调的首选方法是把对一个资源的所有访问集中在一个单独的线程中,然后使用 queue 模块用那个线程服务其他线程的请求。为内部线程通信和协调而使用 Queue 对象的应用程序更易于设计,更可读,并且更可靠。
';

11.3. 使用二进制数据记录布局

最后更新于:2022-04-01 00:49:50

struct 模块为使用变长的二进制记录格式提供了 pack() 和 unpack() 函数。下面的示例演示了在不使用 zipfile 模块的情况下如何迭代一个 ZIP 文件的头信息。压缩码 "H" 和 "I" 分别表示2和4字节无符号数字, " 表明它们都是标准大小并且按照 little-endian 字节排序。 ~~~ import struct with open('myfile.zip', 'rb') as f: data = f.read() start = 0 for i in range(3): # show the first 3 file headers start += 14 fields = struct.unpack(', data[start:start+16]) crc32, comp_size, uncomp_size, filenamesize, extra_size = fields start += 16 filename = data[start:start+filenamesize] start += filenamesize extra = data[start:start+extra_size] print(filename, hex(crc32), comp_size, uncomp_size) start += extra_size + comp_size # skip to the next header ~~~
';

11.2. 模板

最后更新于:2022-04-01 00:49:48

string 提供了一个灵活多变的模版类 Template ,使用它最终用户可以用简单的进行编辑。这使用户可以在不进行改变的情况下定制他们的应用程序。 格式使用 $ 为开头的 Python 合法标识(数字、字母和下划线)作为占位符。占位符外面的大括号使它可以和其它的字符不加空格混在一起。 $ 创建一个单独的 $: ~~~ >>> from string import Template >>> t = Template('${village}folk send $10 to $cause.') >>> t.substitute(village='Nottingham', cause='the ditch fund') 'Nottinghamfolk send $10 to the ditch fund.' ~~~ 当一个占位符在字典或关键字参数中没有被提供时, substitute() 方法就会抛出一个 KeyError 异常。 对于邮件合并风格的应用程序,用户提供的数据可能并不完整,这时使用 safe_substitute()方法可能更适合 — 如果数据不完整,它就不会改变占位符: ~~~ >>> t = Template('Return the $item to $owner.') >>> d = dict(item='unladen swallow') >>> t.substitute(d) Traceback (most recent call last): . . . KeyError: 'owner' >>> t.safe_substitute(d) 'Return the unladen swallow to $owner.' ~~~ 模板子类可以指定一个自定义分隔符。例如,图像查看器的批量重命名工具可能选择使用百分号作为占位符,像当前日期,图片序列号或文件格式: ~~~ >>> import time, os.path >>> photofiles = ['img_1074.jpg', 'img_1076.jpg', 'img_1077.jpg'] >>> class BatchRename(Template): ... delimiter = '%' >>> fmt = input('Enter rename style (%d-date %n-seqnum %f-format): ') Enter rename style (%d-date %n-seqnum %f-format): Ashley_%n%f >>> t = BatchRename(fmt) >>> date = time.strftime('%d%b%y') >>> for i, filename in enumerate(photofiles): ... base, ext = os.path.splitext(filename) ... newname = t.substitute(d=date, n=i, f=ext) ... print('{0} --> {1}'.format(filename, newname)) img_1074.jpg --> Ashley_0.jpg img_1076.jpg --> Ashley_1.jpg img_1077.jpg --> Ashley_2.jpg ~~~ 模板的另一个应用是把多样的输出格式细节从程序逻辑中分类出来。这便使得 XML 文件,纯文本报表和 HTML WEB 报表定制模板成为可能。
';

11.1. 输出格式

最后更新于:2022-04-01 00:49:46

reprlib 模块为大型的或深度嵌套的容器缩写显示提供了 repr() 函数的一个定制版本: ~~~ >>> import reprlib >>> reprlib.repr(set('supercalifragilisticexpialidocious')) "set(['a', 'c', 'd', 'e', 'f', 'g', ...])" ~~~ pprint 模块给老手提供了一种解释器可读的方式深入控制内置和用户自定义对象的打印。当输出超过一行的时候,“美化打印(pretty printer)”添加断行和标识符,使得数据结构显示的更清晰: ~~~ >>> import pprint >>> t = [[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta', ... 'yellow'], 'blue']]] ... >>> pprint.pprint(t, width=30) [[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta', 'yellow'], 'blue']]] ~~~ textwrap 模块格式化文本段落以适应设定的屏宽: ~~~ >>> import textwrap >>> doc = """The wrap() method is just like fill() except that it returns ... a list of strings instead of one big string with newlines to separate ... the wrapped lines.""" ... >>> print(textwrap.fill(doc, width=40)) The wrap() method is just like fill() except that it returns a list of strings instead of one big string with newlines to separate the wrapped lines. ~~~ locale 模块按访问预定好的国家信息数据库。locale 的格式化函数属性集提供了一个直接方式以分组标示格式化数字: ~~~ >>> import locale >>> locale.setlocale(locale.LC_ALL, 'English_United States.1252') 'English_United States.1252' >>> conv = locale.localeconv() # get a mapping of conventions >>> x = 1234567.8 >>> locale.format("%d", x, grouping=True) '1,234,567' >>> locale.format_string("%s%.*f", (conv['currency_symbol'], ... conv['frac_digits'], x), grouping=True) '$1,234,567.80' ~~~
';

11. 标准库浏览 – Part II

最后更新于:2022-04-01 00:49:43

第二部分包含了支持专业编程工作所需的更高级的模块,这些模块很少出现在小脚本中。
';

10.12. “瑞士军刀”

最后更新于:2022-04-01 00:49:41

Python 展现了“瑞士军刀”的哲学。这可以通过它更大的包的高级和健壮的功能来得到最好的展现。列如: * xmlrpc.client 和 xmlrpc.server 模块让远程过程调用变得轻而易举。尽管模块有这样的名字,用户无需拥有 XML 的知识或处理 XML。 * email 包是一个管理邮件信息的库,包括MIME和其它基于 RFC2822 的信息文档。不同于实际发送和接收信息的 smtplib 和 poplib 模块,email 包包含一个构造或解析复杂消息结构(包括附件)及实现互联网编码和头协议的完整工具集。 * xml.dom 和 xml.sax 包为流行的信息交换格式提供了强大的支持。同样, csv 模块支持在通用数据库格式中直接读写。综合起来,这些模块和包大大简化了 Python 应用程序和其它工具之间的数据交换。 * 国际化由 gettext, locale 和 codecs 包支持。
';