10. 标准库简介

10.1. 操作系统接口

os 模块提供了许多与操作系统交互的函数:

>>>
  1. >>> import os
  2. >>> os.getcwd() # Return the current working directory
  3. 'C:\\Python312'
  4. >>> os.chdir('/server/accesslogs') # Change current working directory
  5. >>> os.system('mkdir today') # Run the command mkdir in the system shell
  6. 0
一定要使用 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">import</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">os</font> 而不是 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">from</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">os</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">import</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">*</font> 。这将避免内建的 open() 函数被 os.open() 隐式替换掉,因为它们的使用方式大不相同。 内置的 dir() help() 函数可用作交互式辅助工具,用于处理大型模块,如 os: >>>
  1. >>> import os
  2. >>> dir(os)
  3. <returns a list of all module functions>
  4. >>> help(os)
  5. <returns an extensive manual page created from the module's docstrings>
对于日常文件和目录管理任务, shutil 模块提供了更易于使用的更高级别的接口: >>>
  1. >>> import shutil
  2. >>> shutil.copyfile('data.db', 'archive.db')
  3. 'archive.db'
  4. >>> shutil.move('/build/executables', 'installdir')
  5. 'installdir'

10.2. 文件通配符

glob 模块提供了一个在目录中使用通配符搜索创建文件列表的函数:

>>>
  1. >>> import glob
  2. >>> glob.glob('*.py')
  3. ['primes.py', 'random.py', 'quote.py']

10.3. 命令行参数

一般的工具脚本常常需要处理命令行参数。 这些参数以列表形式存储在 sys 模块的 argv 属性中。 举例来说,让我们查看下面的 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">demo.py</font> 文件:
  1. # File demo.py
  2. import sys
  3. print(sys.argv)
以下是在命令行中运行 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">python</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">demo.py</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">one</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">two</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">three</font> 输出的结果:
  1. ['demo.py', 'one', 'two', 'three']

argparse 模块提供了一种更复杂的机制来处理命令行参数。 以下脚本可提取一个或多个文件名,并可选择要显示的行数:

  1. import argparse
  2. parser = argparse.ArgumentParser(
  3. prog='top',
  4. description='Show top lines from each file')
  5. parser.add_argument('filenames', nargs='+')
  6. parser.add_argument('-l', '--lines', type=int, default=10)
  7. args = parser.parse_args()
  8. print(args)
当在通过 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">python</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">top.py</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">--lines=5</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">alpha.txt</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">beta.txt</font> 在命令行运行时,该脚本会将 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">args.lines</font> 设为 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">5</font> 并将 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">args.filenames</font> 设为 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">['alpha.txt',</font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);"> </font><font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">'beta.txt']</font>

10.4. 错误输出重定向和程序终止

sys 模块还具有 stdin stdout stderr 的属性。后者对于发出警告和错误消息非常有用,即使在 stdout 被重定向后也可以看到它们:

>>>
  1. >>> sys.stderr.write('Warning, log file not found starting a new one\n')
  2. Warning, log file not found starting a new one
终止脚本的最直接方法是使用 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">sys.exit()</font>

10.5. 字符串模式匹配

re 模块为高级字符串处理提供正则表达式工具。对于复杂的匹配和操作,正则表达式提供简洁,优化的解决方案:

>>>
  1. >>> import re
  2. >>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
  3. ['foot', 'fell', 'fastest']
  4. >>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat')
  5. 'cat in the hat'
当只需要简单的功能时,首选字符串方法因为它们更容易阅读和调试: >>>
  1. >>> 'tea for too'.replace('too', 'two')
  2. 'tea for two'

10.6. 数学

The math module gives access to the underlying C library functions for floating-point math: >>>
  1. >>> import math
  2. >>> math.cos(math.pi / 4)
  3. 0.70710678118654757
  4. >>> math.log(1024, 2)
  5. 10.0

random 模块提供了进行随机选择的工具:

>>>
  1. >>> import random
  2. >>> random.choice(['apple', 'pear', 'banana'])
  3. 'apple'
  4. >>> random.sample(range(100), 10) # sampling without replacement
  5. [30, 83, 16, 4, 8, 81, 41, 50, 18, 33]
  6. >>> random.random() # random float
  7. 0.17970987693706186
  8. >>> random.randrange(6) # random integer chosen from range(6)
  9. 4

statistics 模块计算数值数据的基本统计属性(均值,中位数,方差等):

>>>
  1. >>> import statistics
  2. >>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
  3. >>> statistics.mean(data)
  4. 1.6071428571428572
  5. >>> statistics.median(data)
  6. 1.25
  7. >>> statistics.variance(data)
  8. 1.3720238095238095
SciPy项目 <https://scipy.org> 有许多其他模块用于数值计算。

10.7. 互联网访问

有许多模块可用于访问互联网和处理互联网协议。其中两个最简单的 urllib.request 用于从URL检索数据,以及 smtplib 用于发送邮件: >>>
  1. >>> from urllib.request import urlopen
  2. >>> with urlopen('http://worldtimeapi.org/api/timezone/etc/UTC.txt') as response:
  3. ... for line in response:
  4. ... line = line.decode() # Convert bytes to a str
  5. ... if line.startswith('datetime'):
  6. ... print(line.rstrip()) # Remove trailing newline
  7. ...
  8. datetime: 2022-01-01T01:36:47.689215+00:00
  9. >>> import smtplib
  10. >>> server = smtplib.SMTP('localhost')
  11. >>> server.sendmail('soothsayer@example.org', 'jcaesar@example.org',
  12. ... """To: jcaesar@example.org
  13. ... From: soothsayer@example.org
  14. ...
  15. ... Beware the Ides of March.
  16. ... """)
  17. >>> server.quit()
(请注意,第二个示例需要在localhost上运行的邮件服务器。)

10.8. 日期和时间

datetime 模块提供了以简单和复杂的方式操作日期和时间的类。虽然支持日期和时间算法,但实现的重点是有效的成员提取以进行输出格式化和操作。该模块还支持可感知时区的对象。

>>>
  1. >>> # dates are easily constructed and formatted
  2. >>> from datetime import date
  3. >>> now = date.today()
  4. >>> now
  5. datetime.date(2003, 12, 2)
  6. >>> now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of %B.")
  7. '12-02-03. 02 Dec 2003 is a Tuesday on the 02 day of December.'
  8. >>> # dates support calendar arithmetic
  9. >>> birthday = date(1964, 7, 31)
  10. >>> age = now - birthday
  11. >>> age.days
  12. 14368

10.9. 数据压缩

常见的数据存档和压缩格式由模块直接支持,包括:zlib, gzip, bz2, lzma, zipfile tarfile。: >>>
  1. >>> import zlib
  2. >>> s = b'witch which has which witches wrist watch'
  3. >>> len(s)
  4. 41
  5. >>> t = zlib.compress(s)
  6. >>> len(t)
  7. 37
  8. >>> zlib.decompress(t)
  9. b'witch which has which witches wrist watch'
  10. >>> zlib.crc32(s)
  11. 226805979

10.10. 性能测量

一些Python用户对了解同一问题的不同方法的相对性能产生了浓厚的兴趣。 Python提供了一种可以立即回答这些问题的测量工具。 例如,元组封包和拆包功能相比传统的交换参数可能更具吸引力。timeit 模块可以快速演示在运行效率方面一定的优势: >>>
  1. >>> from timeit import Timer
  2. >>> Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()
  3. 0.57535828626024577
  4. >>> Timer('a,b = b,a', 'a=1; b=2').timeit()
  5. 0.54962537085770791
timeit 的精细粒度级别相反, profile pstats 模块提供了用于在较大的代码块中识别时间关键部分的工具。

10.11. 质量控制

开发高质量软件的一种方法是在开发过程中为每个函数编写测试,并在开发过程中经常运行这些测试。

doctest 模块提供了一个工具,用于扫描模块并验证程序文档字符串中嵌入的测试。测试构造就像将典型调用及其结果剪切并粘贴到文档字符串一样简单。这通过向用户提供示例来改进文档,并且它允许doctest模块确保代码保持对文档的真实:

  1. def average(values):
  2. """Computes the arithmetic mean of a list of numbers.
  3. >>> print(average([20, 30, 70]))
  4. 40.0
  5. """
  6. return sum(values) / len(values)
  7. import doctest
  8. doctest.testmod() # automatically validate the embedded tests

unittest 模块不像 doctest 模块那样易于使用,但它允许在一个单独的文件中维护更全面的测试集:

  1. import unittest
  2. class TestStatisticalFunctions(unittest.TestCase):
  3. def test_average(self):
  4. self.assertEqual(average([20, 30, 70]), 40.0)
  5. self.assertEqual(round(average([1, 5, 7]), 1), 4.3)
  6. with self.assertRaises(ZeroDivisionError):
  7. average([])
  8. with self.assertRaises(TypeError):
  9. average(20, 30, 70)
  10. unittest.main() # Calling from the command line invokes all tests

10.12. 自带电池

Python有“自带电池”的理念。通过其包的复杂和强大功能可以最好地看到这一点。例如:
  • xmlrpc.client xmlrpc.server 模块使得实现远程过程调用变成了小菜一碟。 尽管存在于模块名称中,但用户不需要直接了解或处理 XML。
  • email 包是一个用于管理电子邮件的库,包括MIME和其他符合 RFC 2822 规范的邮件文档。与 smtplib poplib 不同(它们实际上做的是发送和接收消息),电子邮件包提供完整的工具集,用于构建或解码复杂的消息结构(包括附件)以及实现互联网编码和标头协议。
  • json 包为解析这种流行的数据交换格式提供了强大的支持。 csv 模块支持以逗号分隔值格式直接读取和写入文件,这种格式通常为数据库和电子表格所支持。 XML 处理由 xml.etree.ElementTree xml.dom xml.sax 包支持。这些模块和软件包共同大大简化了 Python 应用程序和其他工具之间的数据交换。
  • sqlite3 模块是 SQLite 数据库库的包装器,提供了一个可以使用稍微非标准的 SQL 语法更新和访问的持久数据库。
  • 国际化由许多模块支持,包括 gettextlocale ,以及 codecs 包。

11. 标准库简介 —— 第二部分

第二部分涵盖了专业编程所需要的更高级的模块。这些模块很少用在小脚本中。

11.1. 格式化输出

reprlib 模块提供了一个定制化版本的 repr() 函数,用于缩略显示大型或深层嵌套的容器对象:

>>>
  1. >>> import reprlib
  2. >>> reprlib.repr(set('supercalifragilisticexpialidocious'))
  3. "{'a', 'c', 'd', 'e', 'f', 'g', ...}"

pprint 模块提供了更加复杂的打印控制,其输出的内置对象和用户自定义对象能够被解释器直接读取。当输出结果过长而需要折行时,“美化输出机制”会添加换行符和缩进,以更清楚地展示数据结构:

>>>
  1. >>> import pprint
  2. >>> t = [[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta',
  3. ... 'yellow'], 'blue']]]
  4. ...
  5. >>> pprint.pprint(t, width=30)
  6. [[[['black', 'cyan'],
  7. 'white',
  8. ['green', 'red']],
  9. [['magenta', 'yellow'],
  10. 'blue']]]

textwrap 模块能够格式化文本段落,以适应给定的屏幕宽度:

>>>
  1. >>> import textwrap
  2. >>> doc = """The wrap() method is just like fill() except that it returns
  3. ... a list of strings instead of one big string with newlines to separate
  4. ... the wrapped lines."""
  5. ...
  6. >>> print(textwrap.fill(doc, width=40))
  7. The wrap() method is just like fill()
  8. except that it returns a list of strings
  9. instead of one big string with newlines
  10. to separate the wrapped lines.

locale 模块处理与特定地域文化相关的数据格式。locale 模块的 format 函数包含一个 grouping 属性,可直接将数字格式化为带有组分隔符的样式:

>>>
  1. >>> import locale
  2. >>> locale.setlocale(locale.LC_ALL, 'English_United States.1252')
  3. 'English_United States.1252'
  4. >>> conv = locale.localeconv() # get a mapping of conventions
  5. >>> x = 1234567.8
  6. >>> locale.format_string("%d", x, grouping=True)
  7. '1,234,567'
  8. >>> locale.format_string("%s%.*f", (conv['currency_symbol'],
  9. ... conv['frac_digits'], x), grouping=True)
  10. '$1,234,567.80'

11.2. 模板

string 模块包含一个通用的 Template 类,具有适用于最终用户的简化语法。它允许用户在不更改应用逻辑的情况下定制自己的应用。

上述格式化操作是通过占位符实现的,占位符由 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">$</font> 加上合法的 Python 标识符(只能包含字母、数字和下划线)构成。一旦使用花括号将占位符括起来,就可以在后面直接跟上更多的字母和数字而无需空格分割。<font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">$$</font> 将被转义成单个字符 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">$</font>: >>>
  1. >>> from string import Template
  2. >>> t = Template('${village}folk send $$10 to $cause.')
  3. >>> t.substitute(village='Nottingham', cause='the ditch fund')
  4. 'Nottinghamfolk send $10 to the ditch fund.'
如果在字典或关键字参数中未提供某个占位符的值,那么 substitute() 方法将抛出 KeyError。对于邮件合并类型的应用,用户提供的数据有可能是不完整的,此时使用 safe_substitute() 方法更加合适 —— 如果数据缺失,它会直接将占位符原样保留。 >>>
  1. >>> t = Template('Return the $item to $owner.')
  2. >>> d = dict(item='unladen swallow')
  3. >>> t.substitute(d)
  4. Traceback (most recent call last):
  5. ...
  6. KeyError: 'owner'
  7. >>> t.safe_substitute(d)
  8. 'Return the unladen swallow to $owner.'
Template 的子类可以自定义分隔符。例如,以下是某个照片浏览器的批量重命名功能,采用了百分号作为日期、照片序号和照片格式的占位符: >>>
  1. >>> import time, os.path
  2. >>> photofiles = ['img_1074.jpg', 'img_1076.jpg', 'img_1077.jpg']
  3. >>> class BatchRename(Template):
  4. ... delimiter = '%'
  5. ...
  6. >>> fmt = input('Enter rename style (%d-date %n-seqnum %f-format): ')
  7. Enter rename style (%d-date %n-seqnum %f-format): Ashley_%n%f
  8. >>> t = BatchRename(fmt)
  9. >>> date = time.strftime('%d%b%y')
  10. >>> for i, filename in enumerate(photofiles):
  11. ... base, ext = os.path.splitext(filename)
  12. ... newname = t.substitute(d=date, n=i, f=ext)
  13. ... print('{0} --> {1}'.format(filename, newname))
  14. img_1074.jpg --> Ashley_0.jpg
  15. img_1076.jpg --> Ashley_1.jpg
  16. img_1077.jpg --> Ashley_2.jpg
模板的另一个应用是将程序逻辑与多样的格式化输出细节分离开来。这使得对 XML 文件、纯文本报表和 HTML 网络报表使用自定义模板成为可能。

11.3. 使用二进制数据记录格式

struct 模块提供了 pack() unpack() 函数,用于处理不定长度的二进制记录格式。下面的例子展示了在不使用 zipfile 模块的情况下,如何循环遍历一个 ZIP 文件的所有头信息。Pack 代码 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">"H"</font> <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">"I"</font> 分别代表两字节和四字节无符号整数。<font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">"<"</font> 代表它们是标准尺寸的小端字节序:

  1. import struct
  2. with open('myfile.zip', 'rb') as f:
  3. data = f.read()
  4. start = 0
  5. for i in range(3): # show the first 3 file headers
  6. start += 14
  7. fields = struct.unpack('<IIIHH', data[start:start+16])
  8. crc32, comp_size, uncomp_size, filenamesize, extra_size = fields
  9. start += 16
  10. filename = data[start:start+filenamesize]
  11. start += filenamesize
  12. extra = data[start:start+extra_size]
  13. print(filename, hex(crc32), comp_size, uncomp_size)
  14. start += extra_size + comp_size # skip to the next header

11.4. 多线程

线程是一种对于非顺序依赖的多个任务进行解耦的技术。多线程可以提高应用的响应效率,当接收用户输入的同时,保持其他任务在后台运行。一个有关的应用场景是,将 I/O 和计算运行在两个并行的线程中。 以下代码展示了高阶的 threading 模块如何在后台运行任务,且不影响主程序的继续运行:
  1. import threading, zipfile
  2. class AsyncZip(threading.Thread):
  3. def __init__(self, infile, outfile):
  4. threading.Thread.__init__(self)
  5. self.infile = infile
  6. self.outfile = outfile
  7. def run(self):
  8. f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED)
  9. f.write(self.infile)
  10. f.close()
  11. print('Finished background zip of:', self.infile)
  12. background = AsyncZip('mydata.txt', 'myarchive.zip')
  13. background.start()
  14. print('The main program continues to run in foreground.')
  15. background.join() # Wait for the background task to finish
  16. print('Main program waited until background was done.')
多线程应用面临的主要挑战是,相互协调的多个线程之间需要共享数据或其他资源。为此,threading 模块提供了多个同步操作原语,包括线程锁、事件、条件变量和信号量。 尽管这些工具非常强大,但微小的设计错误却可以导致一些难以复现的问题。因此,实现多任务协作的首选方法是将所有对资源的请求集中到一个线程中,然后使用 queue 模块向该线程供应来自其他线程的请求。 应用程序使用 Queue 对象进行线程间通信和协调,更易于设计,更易读,更可靠。

11.5. 日志记录

logging 模块提供功能齐全且灵活的日志记录系统。在最简单的情况下,日志消息被发送到文件或 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">sys.stderr</font>

  1. import logging
  2. logging.debug('Debugging information')
  3. logging.info('Informational message')
  4. logging.warning('Warning:config file %s not found', 'server.conf')
  5. logging.error('Error occurred')
  6. logging.critical('Critical error -- shutting down')
这会产生以下输出:
  1. WARNING:root:Warning:config file server.conf not found
  2. ERROR:root:Error occurred
  3. CRITICAL:root:Critical error -- shutting down
默认情况下,informational 和 debugging 消息被压制,输出会发送到标准错误流。其他输出选项包括将消息转发到电子邮件,数据报,套接字或 HTTP 服务器。新的过滤器可以根据消息优先级选择不同的路由方式:DEBUGINFOWARNINGERROR,和 CRITICAL 日志系统可以直接从 Python 配置,也可以从用户配置文件加载,以便自定义日志记录而无需更改应用程序。

11.6. 弱引用

Python 会自动进行内存管理(对大多数对象进行引用计数并使用 garbage collection 来清除循环引用)。 当某个对象的最后一个引用被移除后不久就会释放其所占用的内存。 此方式对大多数应用来说都适用,但偶尔也必须在对象持续被其他对象所使用时跟踪它们。 不幸的是,跟踪它们将创建一个会令其永久化的引用。 weakref 模块提供的工具可以不必创建引用就能跟踪对象。 当对象不再需要时,它将自动从一个弱引用表中被移除,并为弱引用对象触发一个回调。 典型应用包括对创建开销较大的对象进行缓存: >>>
  1. >>> import weakref, gc
  2. >>> class A:
  3. ... def __init__(self, value):
  4. ... self.value = value
  5. ... def __repr__(self):
  6. ... return str(self.value)
  7. ...
  8. >>> a = A(10) # create a reference
  9. >>> d = weakref.WeakValueDictionary()
  10. >>> d['primary'] = a # does not create a reference
  11. >>> d['primary'] # fetch the object if it is still alive
  12. 10
  13. >>> del a # remove the one reference
  14. >>> gc.collect() # run garbage collection right away
  15. 0
  16. >>> d['primary'] # entry was automatically removed
  17. Traceback (most recent call last):
  18. File "<stdin>", line 1, in <module>
  19. d['primary'] # entry was automatically removed
  20. File "C:/python312/lib/weakref.py", line 46, in __getitem__
  21. o = self.data[key]()
  22. KeyError: 'primary'

11.7. 用于操作列表的工具

许多对于数据结构的需求可以通过内置列表类型来满足。 但是,有时也会需要具有不同效费比的替代实现。

array 模块提供了一种 array 对象,它类似于列表,但只能存储类型一致的数据且存储密度更高。 下面的例子显示了一个由存储为双字节无符号整数的数字 (类型码 <font style="color:rgb(0, 0, 0);background-color:rgb(236, 240, 243);">"H"</font>) 组成的元组,而不是常规 Python int 对象列表所采用的每个条目 16 字节:

>>>
  1. >>> from array import array
  2. >>> a = array('H', [4000, 10, 700, 22222])
  3. >>> sum(a)
  4. 26932
  5. >>> a[1:3]
  6. array('H', [10, 700])

collections 模块提供了一种 deque 对象,它类似于列表,但从左端添加和弹出的速度较快而在中间查找的速度较慢。 此种对象适用于实现队列和广度优先树搜索:

>>>
  1. >>> from collections import deque
  2. >>> d = deque(["task1", "task2", "task3"])
  3. >>> d.append("task4")
  4. >>> print("Handling", d.popleft())
  5. Handling task1
  1. unsearched = deque([starting_node])
  2. def breadth_first_search(unsearched):
  3. node = unsearched.popleft()
  4. for m in gen_moves(node):
  5. if is_goal(m):
  6. return m
  7. unsearched.append(m)
在替代的列表实现以外,标准库也提供了其他工具,例如 bisect 模块具有用于操作有序列表的函数: >>>
  1. >>> import bisect
  2. >>> scores = [(100, 'perl'), (200, 'tcl'), (400, 'lua'), (500, 'python')]
  3. >>> bisect.insort(scores, (300, 'ruby'))
  4. >>> scores
  5. [(100, 'perl'), (200, 'tcl'), (300, 'ruby'), (400, 'lua'), (500, 'python')]

heapq 模块提供了基于常规列表来实现堆的函数。 最小值的条目总是保持在位置零。 这对于需要重复访问最小元素而不希望运行完整列表排序的应用来说非常有用:

>>>
  1. >>> from heapq import heapify, heappop, heappush
  2. >>> data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
  3. >>> heapify(data) # rearrange the list into heap order
  4. >>> heappush(data, -5) # add a new entry
  5. >>> [heappop(data) for i in range(3)] # fetch the three smallest entries
  6. [-5, 0, 1]

11.8. Decimal Floating-Point Arithmetic

The decimal module offers a Decimal datatype for decimal floating-point arithmetic. Compared to the built-in float implementation of binary floating point, the class is especially helpful for
  • 财务应用和其他需要精确十进制表示的用途,
  • 控制精度,
  • 控制四舍五入以满足法律或监管要求,
  • 跟踪有效小数位,或
  • 用户期望结果与手工完成的计算相匹配的应用程序。
例如,使用十进制浮点和二进制浮点数计算70美分手机和5%税的总费用,会产生的不同结果。如果结果四舍五入到最接近的分数差异会更大: >>>
  1. >>> from decimal import *
  2. >>> round(Decimal('0.70') * Decimal('1.05'), 2)
  3. Decimal('0.74')
  4. >>> round(.70 * 1.05, 2)
  5. 0.73

Decimal 表示的结果会保留尾部的零,并根据具有两个有效位的被乘数自动推出四个有效位。 Decimal 可以模拟手工运算来避免当二进制浮点数无法精确表示十进制数时会导致的问题。

精确表示特性使得 Decimal 类能够执行对于二进制浮点数来说不适用的模运算和相等性检测: >>>
  1. >>> Decimal('1.00') % Decimal('.10')
  2. Decimal('0.00')
  3. >>> 1.00 % 0.10
  4. 0.09999999999999995
  5. >>> sum([Decimal('0.1')]*10) == Decimal('1.0')
  6. True
  7. >>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 == 1.0
  8. False

decimal 模块提供了运算所需要的足够精度:

>>>
  1. >>> getcontext().prec = 36
  2. >>> Decimal(1) / Decimal(7)
  3. Decimal('0.142857142857142857142857142857142857')