Python 3.2 有什么新变化

作者:

Raymond Hettinger(译者:wh2099 at outlook dot com)

这篇文章介绍了 Python 3.2 相比 3.1 新增的特性。 Python 3.2 发布于 2011 年 2 月 20 日。 文章聚焦于几个关键特性并给出了一些示例。 有关完整细节,请参阅 Misc/NEWS 文件。

参见

PEP 392 - Python 3.2 发布计划

PEP 384: 定义稳定的ABI

过去,为一个 Python 版本所构建的扩展模块通常无法用于其他 Python 版本。 特别是在 Windows 上,每一个 Python 新特性发布版都必须重新构建想要使用的所有扩展模块。 之所以有这样的要求是因为扩展模块可以任意访问 Python 解释器的内部对象。

在 Python 3.2 中,则有了一种替代方式:扩展模块将自己约束于一个受限 API(通过定义 Py_LIMITED_API)因而不能使用许多内部对象,仅限使用一组承诺会在多个发布版中保持稳定的 API 函数。 作为其结果,在这种模式下为 3.2 构建的扩展模块也将能在 3.3、3.4 等版本中运行。 使用了内存结构体细节数据的扩展模块仍然可以被构建,但将需要为每个新特性发布版重新编译。

参见

PEP 384 - 定义稳定的ABI

PEP 由 Martin von Löwis 撰写

PEP 389: Argparse 命令行解析模块

引入了一个新的 argparse 模块用于命令行解析,以克服 optparse 的局限性,后者不支持位置参数(不仅仅是选项)、子命令、必需选项以及其他常见的选项指定和验证模式。

该模块作为一个第三方模块已在社区中取得了广泛的成功。相比其前身,argparse 模块功能更加全面,现在是处理命令行的首选模块。出于对大量依赖该模块的遗留代码的考虑,旧模块仍被保留使用。

以下是一个带注释的示例解析器,展示了诸如限制结果到一组选项、在帮助屏幕中指定 metavar、验证一个或多个位置参数是否存在以及创建一个必需选项等功能:

import argparse
parser = argparse.ArgumentParser(
            description = '管理服务器',         # 帮助中的主要描述
            epilog = '已在Solaris和Linux上测试') # 要求帮助后显示的内容
parser.add_argument('action',                       # 参数名称
            choices = ['deploy', 'start', 'stop'],  # 三个允许的值
            help = '对每个目标执行的操作')         # 帮助信息
parser.add_argument('targets',
            metavar = 'HOSTNAME',                   # 帮助信息中使用的变量名
            nargs = '+',                            # 需要一个或多个目标
            help = '目标机器的URL')               # 帮助信息解释
parser.add_argument('-u', '--user',                 # -u 或 --user 选项
            required = True,                        # 使其成为必需参数
            help = '以用户身份登录')

在命令字符串中调用解析器的示例:

>>> cmd = 'deploy sneezy.example.com sleepy.example.com -u skycaptain'
>>> result = parser.parse_args(cmd.split())
>>> result.action
'deploy'
>>> result.targets
['sneezy.example.com', 'sleepy.example.com']
>>> result.user
'skycaptain'

解释器自动生成的帮助示例:

>>> parser.parse_args('-h'.split())

usage: manage_cloud.py [-h] -u USER
                       {deploy,start,stop} HOSTNAME [HOSTNAME ...]

Manage servers

positional arguments:
  {deploy,start,stop}   action on each target
  HOSTNAME              url for target machines

optional arguments:
  -h, --help            show this help message and exit
  -u USER, --user USER  login as user

Tested on Solaris and Linux

一个非常好的 argparse 特性是可以定义子解析器,每个子解析器拥有它们自己的参数模式和帮助显示:

import argparse
parser = argparse.ArgumentParser(prog='HELM')
subparsers = parser.add_subparsers()

parser_l = subparsers.add_parser('launch', help='Launch Control')   # 第一个子分组
parser_l.add_argument('-m', '--missiles', action='store_true')
parser_l.add_argument('-t', '--torpedos', action='store_true')

parser_m = subparsers.add_parser('move', help='Move Vessel',        # 第二个子分组
                                 aliases=('steer', 'turn'))         # 等价的名称
parser_m.add_argument('-c', '--course', type=int, required=True)
parser_m.add_argument('-s', '--speed', type=int, default=0)
$ ./helm.py --help                         # 最高层级的帮助 (launch 和 move)
$ ./helm.py launch --help                  # launch 选项的帮助
$ ./helm.py launch --missiles              # 设置 missiles=True 及 torpedos=False
$ ./helm.py steer --course 180 --speed 5   # 设置动作形参

参见

PEP 389 - 新的命令行解析模块

PEP 由 Steven Bethard 撰写

参阅 将 optparse 代码迁移至 argparse 了解与 optparse 的差异的细节。

PEP 391: 基于字典的日志配置

logging 模块提供了两种配置方式,一种是通过函数调用为每个选项进行配置,另一种是通过外部文件进行配置,该文件以 configparser 格式保存。这些选项无法灵活地从 JSON 或 YAML 文件创建配置,也不支持增量配置,而增量配置是用于从命令行指定日志记录器选项所必需的。

为了支持更灵活的配置方式,该模块现在提供了 logging.config.dictConfig(),用于通过普通的 Python 字典指定日志配置。配置选项包括格式化器、处理器、过滤器和日志记录器。以下是一个配置字典的工作示例:

{"version": 1,
 "formatters": {"brief": {"format": "%(levelname)-8s: %(name)-15s: %(message)s"},
                "full": {"format": "%(asctime)s %(name)-15s %(levelname)-8s %(message)s"}
                },
 "handlers": {"console": {
                   "class": "logging.StreamHandler",
                   "formatter": "brief",
                   "level": "INFO",
                   "stream": "ext://sys.stdout"},
              "console_priority": {
                   "class": "logging.StreamHandler",
                   "formatter": "full",
                   "level": "ERROR",
                   "stream": "ext://sys.stderr"}
              },
 "root": {"level": "DEBUG", "handlers": ["console", "console_priority"]}}

如果将该字典存储在一个名为 conf.json 的文件中,可以使用如下代码加载并调用它:

>>> import json, logging.config
>>> with open('conf.json') as f:
...     conf = json.load(f)
...
>>> logging.config.dictConfig(conf)
>>> logging.info("Transaction completed normally")
INFO    : root           : Transaction completed normally
>>> logging.critical("Abnormal termination")
2011-02-17 11:14:36,694 root            CRITICAL Abnormal termination

参见

PEP 391 - 基于字典的日志配置

PEP 由 Vinay Sajip 撰写

PEP 3148: concurrent.futures 模块

用于创建和管理并发性的代码正在被收集到一个新的顶级命名空间 concurrent 中。其第一个成员是一个 futures 包,该包提供了一个统一的高级接口,用于管理线程和进程。

concurrent.futures 的设计灵感来自 java.util.concurrent 包。在该模型中,一个正在运行的调用及其结果由一个 Future 对象表示,该对象抽象了线程、进程和远程过程调用共有的特性。该对象支持状态检查(运行中或已完成)、超时、取消、添加回调以及访问结果或异常。

新模块的核心功能是提供了一对执行器类,用于启动和管理调用任务。这些执行器的设计目标是简化现有并行调用工具的使用流程,它们能够帮助开发者省去以下繁琐操作:配置资源池、发起调用任务、创建结果队列、添加超时处理机制,以及限制线程、进程或远程过程调用的总数量。

理想情况下,每个应用程序应该在多个组件之间共享单个执行器,以便可以集中管理进程和线程限制。这解决了当每个组件都有自己的资源管理竞争策略时出现的设计挑战。

这两个类共享一个通用接口,包含三个方法:submit() 用于调度可调用对象并返回一个 Future 对象;map() 用于一次性调度多个异步调用;shutdown() 用于释放资源。该类是一个 上下文管理器,可以在 with 语句中使用,以确保在当前挂起的futures对象执行完毕时自动释放资源。

ThreadPoolExecutor 的一个简单示例是启动四个并行线程来复制文件:

import concurrent.futures, shutil
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as e:
    e.submit(shutil.copy, 'src1.txt', 'dest1.txt')
    e.submit(shutil.copy, 'src2.txt', 'dest2.txt')
    e.submit(shutil.copy, 'src3.txt', 'dest3.txt')
    e.submit(shutil.copy, 'src3.txt', 'dest4.txt')

参见

PEP 3148 -- futures - 异步执行指令

PEP 由 Brian Quinlan 撰写

使用线程并行 URL 读取的代码示例,展示了如何利用线程并发获取多个网页内容。

并行计算质数的代码示例,展示了 ProcessPoolExecutor 的用法。

PEP 3147: PYC 仓库目录

Python 在 .pyc 文件中缓存字节码的方案在存在多个 Python 解释器的环境中效果不佳。如果一个解释器遇到了由另一个解释器创建的缓存文件,它会重新编译源代码并覆盖缓存文件,从而失去缓存的优势。

随着 Linux 发行版普遍附带多个 Python 版本,“pyc 冲突”问题变得更加突出。这些冲突在使用 CPython 的替代品(如 Unladen Swallow)时也会出现。

为解决此问题,Python 的导入机制已进行扩展,会为每个解释器使用不同的文件名。Python 3.2、Python 3.3 和 Unladen Swallow 不再争抢同一个名为 "mymodule.pyc" 的文件,而是分别查找 "mymodule.cpython-32.pyc"、"mymodule.cpython-33.pyc" 和 "mymodule.unladen10.pyc"。此外,为避免这些新文件使源目录变得杂乱,pyc 文件现在会被收集到一个名为 "__pycache__" 的目录中,该目录存放在包目录下。

除了文件名和目标目录之外,新方案还有几个方面是程序员可见的:

  • 导入的模块现在具有一个 __cached__ 属性,该属性存储实际导入的文件名:

    >>> import collections
    >>> collections.__cached__
    'c:/py32/lib/__pycache__/collections.cpython-32.pyc'
    
  • 可以通过 imp 模块访问每个解释器唯一的标签:

    >>> import imp
    >>> imp.get_tag()
    'cpython-32'
    
  • 尝试从导入的文件推断源文件名的脚本现在需要更智能。简单地从".pyc"文件名中去除"c"已不再足够。相反,应使用 imp 模块中的新函数:

    >>> imp.source_from_cache('c:/py32/lib/__pycache__/collections.cpython-32.pyc')
    'c:/py32/lib/collections.py'
    >>> imp.cache_from_source('c:/py32/lib/collections.py')
    'c:/py32/lib/__pycache__/collections.cpython-32.pyc'
    
  • py_compilecompileall 模块已更新以反映新的命名约定和目标目录。compileall 的命令行调用有了新选项:-i 用于指定要编译的文件和目录列表,-b 使字节码文件写入其传统位置而非 __pycache__

  • importlib.abc 模块已更新,包含用于加载字节码文件的新 抽象基类。已弃用的ABCs,PyLoaderPyPycLoader (如何保持与 Python 3.1 兼容的说明包含在文档中) 。

参见

PEP 3147 - PYC 仓库目录

PEP 由 Barry Warsaw 撰写

PEP 3149: 带有 ABI 版本标签的 .so 文件

PYC存储库目录允许多个字节码缓存文件共存。此PEP通过为共享对象文件提供一个公共目录并为每个版本提供不同的名称,实现了类似的机制。

通用目录为 "pyshared",文件名通过识别 Python 实现方式(如 CPython、PyPy、Jython 等)、主版本号和次版本号以及可选的构建标志(如 "d" 表示调试、"m" 表示 pymalloc、"u" 表示宽Unicode)来区分。对于任意包 "foo",当分发包安装时,您可能会看到这些文件:

/usr/share/pyshared/foo.cpython-32m.so
/usr/share/pyshared/foo.cpython-33md.so

对于 Python 本身,可以通过 sysconfig 模块中的函数来访问这些标签:

>>> import sysconfig
>>> sysconfig.get_config_var('SOABI')       # 查找版本标签
'cpython-32mu'
>>> sysconfig.get_config_var('EXT_SUFFIX')  # 查找完整文件名扩展
'.cpython-32mu.so'

参见

PEP 3149 - 带有 ABI 版本标签的 .so 文件

PEP 由 Barry Warsaw 撰写

PEP 3333: Python Web服务器网关接口v1.0.1

本信息性 PEP 阐明了 WSGI 协议如何处理字节/文本问题。挑战在于,尽管 HTTP 协议本身是基于字节的,但 Python 3 中的字符串处理最方便的方式是使用 str 类型。

该 PEP 规范区分了用于请求/响应头和元数据的所谓 原生字符串 (native strings),与用于请求和响应正文的 字节字符串 (byte strings)。

原生字符串 始终为 str 类型,但限制在 U+0000U+00FF 之间的代码点,这些代码点可以使用 Latin-1 编码转换为字节。这些字符串用于环境字典中的键和值,以及 start_response() 函数中的响应头和状态。它们必须遵循 RFC 2616 的编码要求。也就是说,它们必须是 ISO-8859-1 字符或使用 RFC 2047 MIME 编码。

对于从 Python 2 迁移 WSGI 应用程序的开发者,以下是关键点:

  • 如果应用程序在 Python 2 中已经使用字符串作为请求/响应头,则无需更改。

  • 如果应用程序对输出头进行编码或对输入头进行解码,则头需要重新编码为 Latin-1。例如,一个使用 utf-8 编码的输出头原本使用 h.encode('utf-8'),现在需要转换为字节到原生字符串,使用 h.encode('utf-8').decode('latin-1')

  • 应用程序生成的值或使用 write() 方法发送的值必须是字节字符串。start_response() 函数和环境必须使用原生字符串。两者不能混用。

对于编写CGI到WSGI路径或其他CGI风格协议的服务器实现者来说,用户必须能够使用原生字符串访问环境,即使底层平台可能有不同的约定。为了弥合这一差距,wsgiref 模块新增了一个函数 wsgiref.handlers.read_environ(),用于将CGI变量从 os.environ 转码为原生字符串,并返回一个新的字典。

参见

PEP 3333 - Python Web服务器网关接口v1.0.1

PEP 由 Phillip Eby 撰写

其他语言特性修改

对Python 语言核心进行的小改动:

  • format() 函数和 str.format() 方法的字符串格式化功能为格式字符 # 新增了能力。此前,对于二进制、八进制或十六进制的整数,该字符会使输出分别带有 '0b'、'0o' 或 '0x' 前缀。现在,它还能处理浮点数、复数和 Decimal 类型,确保输出始终包含小数点——即使后续没有数字跟随。

    >>> format(20, '#o')
    '0o24'
    >>> format(12.34, '#5.0f')
    '  12.'
    

    (由 Mark Dickinson 建议,Eric Smith 在 bpo-7094 中实现。)

  • There is also a new str.format_map() method that extends the capabilities of the existing str.format() method by accepting arbitrary mapping objects. This new method makes it possible to use string formatting with any of Python's many dictionary-like objects such as defaultdict, Shelf, ConfigParser, or dbm. It is also useful with custom dict subclasses that normalize keys before look-up or that supply a __missing__() method for unknown keys:

    >>> import shelve
    >>> d = shelve.open('tmp.shl')
    >>> 'The {project_name} status is {status} as of {date}'.format_map(d)
    'The testing project status is green as of February 15, 2011'
    
    >>> class LowerCasedDict(dict):
    ...     def __getitem__(self, key):
    ...         return dict.__getitem__(self, key.lower())
    ...
    >>> lcd = LowerCasedDict(part='widgets', quantity=10)
    >>> 'There are {QUANTITY} {Part} in stock'.format_map(lcd)
    'There are 10 widgets in stock'
    
    >>> class PlaceholderDict(dict):
    ...     def __missing__(self, key):
    ...         return '<{}>'.format(key)
    ...
    >>> 'Hello {name}, welcome to {location}'.format_map(PlaceholderDict())
    'Hello <name>, welcome to <location>'
    

(由 Raymond Hettinger 提议并由 Eric Smith 在 bpo-6081 中贡献。)

  • 解释器现在可以带有一个安静选项 -q 启动,以防止在交互模式下显示版权和版本信息。该选项可以通过 sys.flags 属性进行内省:

    $ python -q
    >>> sys.flags
    sys.flags(debug=0, division_warning=0, inspect=0, interactive=0,
    optimize=0, dont_write_bytecode=0, no_user_site=0, no_site=0,
    ignore_environment=0, verbose=0, bytes_warning=0, quiet=1)
    

    (由 Marcin Wojdyr 在 bpo-1772833 中贡献。)

  • hasattr() 函数通过调用 getattr() 并检测是否引发异常来工作。这种技术使其能够检测由 __getattr__()__getattribute__() 动态创建的方法,这些方法在类字典中原本是不存在的。以前,hasattr 会捕获任何异常,可能会掩盖真正的错误。现在,hasattr 已经被改进,仅捕获 AttributeError,而让其他异常通过:

    >>> class A:
    ...     @property
    ...     def f(self):
    ...         return 1 // 0
    ...
    >>> a = A()
    >>> hasattr(a, 'f')
    Traceback (most recent call last):
      ...
    ZeroDivisionError: integer division or modulo by zero
    

    (由 Yury Selivanov 发现并由 Benjamin Peterson 修正;bpo-9666。)

  • 浮点数或复数的 str() 现在与它的 repr() 相同。以前,str() 形式更短,但这只会造成混淆,因此现在默认显示最短的 repr():

    >>> import math
    >>> repr(math.pi)
    '3.141592653589793'
    >>> str(math.pi)
    '3.141592653589793'
    

    (由 Mark Dickinson 提议并实现;bpo-9337。)

  • memoryview 对象现在有一个 release() 方法,并且它们现在也支持上下文管理协议。这允许及时释放请求原始对象缓冲区时获得的任何资源。

    >>> with memoryview(b'abcdefgh') as v:
    ...     print(v.tolist())
    [97, 98, 99, 100, 101, 102, 103, 104]
    

    (由 Antoine Pitrou 添加;bpo-9757。)

  • 此前,如果名称在嵌套块中作为自由变量出现,删除局部命名空间中的名称是非法的:

    def outer(x):
        def inner():
            return x
        inner()
        del x
    

    现在允许这样做。请记住,except 子句的目标是被清除的,所以这段代码在 Python 2.6 中可以工作,但在 Python 3.1 中会引发 SyntaxError,现在又再次可以工作:

    def f():
        def print_error():
            print(e)
        try:
            something
        except Exception as e:
            print_error()
            # 在此隐式执行 "del e"
    

    (参见 bpo-4617。)

  • 结构体序列类型 现在是 tuple 的子类。这意味着像 os.stat()time.gmtime()sys.version_info 返回的 C 结构现在像 具名元组 一样工作,并且可以与期望一个元组作为参数的函数和方法一起使用。这是使 C 结构体与其纯 Python 对应物一样灵活的一大步:

    >>> import sys
    >>> isinstance(sys.version_info, tuple)
    True
    >>> 'Version %d.%d.%d %s(%d)' % sys.version_info
    'Version 3.2.0 final(0)'
    

    (由 Arfrever Frehtes Taifersar Arahesis 建议,Benjamin Peterson 在 bpo-8413 中实现。)

  • 现在可以使用 PYTHONWARNINGS 环境变量来更轻松地控制警告,作为在命令行中使用 -W 的替代方案:

    $ export PYTHONWARNINGS='ignore::RuntimeWarning::,once::UnicodeWarning::'
    

    (由Barry Warsaw建议,Philip Jenvey在 bpo-7301 中实现。)

  • 新增了一个警告类别: ResourceWarning。当检测到资源消耗或清理的潜在问题时会发出此警告。在正常发布版本中,该警告默认被禁用,但可以通过 warnings 模块提供的方式或通过命令行启用。

    如果在解释器关闭时 gc.garbage 列表不为空,并且设置了 gc.DEBUG_UNCOLLECTABLE,则会发出 ResourceWarning,并打印所有不可收集的对象。这是为了提醒程序员他们的代码中存在对象终结问题。

    当一个 文件对象 在没有显式关闭的情况下被销毁时,也会发出 ResourceWarning。尽管此类对象的析构函数确保关闭底层的操作系统资源(通常是文件描述符),但对象释放的延迟可能导致各种问题,特别是在 Windows 系统下。以下是通过命令行启用该警告的示例:

    $ python -q -Wdefault
    >>> f = open("foo", "wb")
    >>> del f
    __main__:1: ResourceWarning: unclosed file <_io.BufferedWriter name='foo'>
    

    (由 Antoine Pitrou 和 Georg Brandl 在 bpo-10093bpo-477863 中添加。)

  • range 对象现在支持 indexcount 方法。这是为了让更多对象完全实现 collections.Sequence 抽象基类 而进行的工作的一部分。因此,该语言将拥有更加统一的 API。此外,range 对象现在还支持切片操作和负数索引,即使这些索引的值大于 sys.maxsize。这使得 range 与列表具有更好的互操作性:

    >>> range(0, 100, 2).count(10)
    1
    >>> range(0, 100, 2).index(10)
    5
    >>> range(0, 100, 2)[5]
    10
    >>> range(0, 100, 2)[0:5]
    range(0, 10, 2)
    

    (由 Daniel Stutzbach 在 bpo-9213 中贡献,由 Alexander Belopolsky 在 bpo-2690 中贡献,由 Nick Coghlan 在 bpo-10889 中贡献。)

  • callable() 内置函数从 Py2.x 中恢复。它提供了一个简洁、可读的替代方案,用于在类似 isinstance(x, collections.Callable) 的表达式中使用 抽象基类

    >>> callable(max)
    True
    >>> callable(20)
    False
    

    (参见 bpo-10518。)

  • Python 的导入机制现在可以加载路径名中包含非 ASCII 字符的目录中安装的模块。这解决了一个令人烦恼的问题,即用户主目录中包含非 ASCII 字符的用户名。

(需要 Victor Stinner 在 bpo-9425 中做大量工作。)

新增,改进和弃用的模块

Python 标准库经过了大量的维护工作和质量改进。

Python 3.2 最重要的更新是:email 包、mailbox 模块以及 nntplib 模块现在能够正确支持 Python 3 的字节/文本模型。这是首次实现对混合编码邮件的正确处理。

在整个标准库中,对编码和文本与字节问题的关注更加细致。特别是,与操作系统的交互现在能够更好地使用 Windows MBCS 编码、区域感知编码或 UTF-8 交换非 ASCII 数据。

另一个重要的改进是大幅提升了 SSL 连接和安全证书的支持。

此外,更多的类现在实现了 上下文管理器,以支持使用 with 语句进行方便且可靠的资源清理。

email

Python 3 中 email 包的可用性已基本由 R. David Murray 的极大努力修复。问题在于电子邮件通常以 bytes 而不是 str 文本的形式读取和存储,并且单个电子邮件中可能包含多种编码。因此,必须扩展电子邮件包以解析和生成字节格式的电子邮件消息。

  • 新函数 message_from_bytes()message_from_binary_file(),以及新类 BytesFeedParserBytesParser 允许将二进制消息数据解析为模型对象。

  • 给定模型的字节输入,get_payload() 默认将解码具有 Content-Transfer-Encoding8bit 的消息正文,使用 MIME 头中指定的字符集,并返回结果字符串。

  • 给定模型的字节输入,Generator 将把具有 Content-Transfer-Encoding8bit 的消息正文转换为具有 7bitContent-Transfer-Encoding

    包含未编码的非 ASCII 字节的邮件头,将被视为使用 unknown-8bit 字符集按照 RFC 2047 标准进行编码。

  • 一个新的类 BytesGenerator 生成字节输出,保留输入中未更改的任何非 ASCII 数据,包括具有 Content-Transfer-Encoding8bit 的消息正文。

  • smtplibSMTP 类现在接受字节字符串作为 sendmail() 方法的 msg 参数,并且一个新的方法 send_message() 接受一个 Message 对象,并可以可选地从对象中直接获取 from_addrto_addrs 地址。

(由 R. David Murray 在 bpo-4661bpo-10321 中提议并实现。)

elementtree

xml.etree.ElementTree 包及其 xml.etree.cElementTree 对应版本已更新到 1.3。

新增了几个有用的函数和方法:

两个方法被弃用:

  • xml.etree.ElementTree.getchildren() 使用 list(elem) 代替。

  • xml.etree.ElementTree.getiterator() 使用 Element.iter 代替。

有关更新的详细信息,请参阅 Fredrik Lundh 网站上的 Introducing ElementTree

(由 Florent Xicluna 和 Fredrik Lundh 在 bpo-6472 中贡献。)

functools

  • functools 模块包含一个新的装饰器用于缓存函数调用。functools.lru_cache() 可以在预期结果相同的情况下,保存对外部资源的重复查询。

    例如,向数据库查询函数添加缓存装饰器可以节省热门搜索的数据库访问:

    >>> import functools
    >>> @functools.lru_cache(maxsize=300)
    ... def get_phone_number(name):
    ...     c = conn.cursor()
    ...     c.execute('SELECT phonenumber FROM phonelist WHERE name=?', (name,))
    ...     return c.fetchone()[0]
    
    >>> for name in user_requests:
    ...     get_phone_number(name)        # cached lookup
    

    为了帮助选择有效的缓存大小,包装函数被用于跟踪缓存统计信息:

    >>> get_phone_number.cache_info()
    CacheInfo(hits=4805, misses=980, maxsize=300, currsize=300)
    

    如果 phonelist 表被更新,过时的缓存内容可以通过以下方式清除:

    >>> get_phone_number.cache_clear()
    

    (由 Raymond Hettinger 贡献,并融合了 Jim Baker、Miki Tebeka 和 Nick Coghlan 的设计理念;参见 recipe 498245recipe 577479bpo-10586bpo-10593。)

  • functools.wraps() 装饰器现在添加了一个指向原始可调用函数的 __wrapped__ 属性。这使得包装函数可以被内省。它还会复制定义的 __annotations__。现在它也能优雅地跳过缺失的属性,如可能未为包装的可调用对象定义的 __doc__

    在上面的例子中,可以通过恢复原始函数来移除缓存:

    >>> get_phone_number = get_phone_number.__wrapped__    # uncached function
    

    (由 Nick Coghlan 和 Terrence Cole 在 bpo-9567, bpo-3445bpo-8814 中贡献。)

  • 为帮助编写具有丰富比较方法的类,新增的装饰器 functools.total_ordering() 将使用现有的相等和不相等方法来填充其余的方法。

    例如,提供 __eq__ and __lt__ 将启用 total_ordering() 来填充 __le__, __gt____ge__:

    @total_ordering
    class Student:
        def __eq__(self, other):
            return ((self.lastname.lower(), self.firstname.lower()) ==
                    (other.lastname.lower(), other.firstname.lower()))
    
        def __lt__(self, other):
            return ((self.lastname.lower(), self.firstname.lower()) <
                    (other.lastname.lower(), other.firstname.lower()))
    

    使用 total_ordering 装饰器时,将会自动填充其余的比较方法。

    (由 Raymond Hettinger 贡献。)

  • 为帮助移植 Python 2 程序,functools.cmp_to_key() 函数可将旧式的比较函数转换为新式的 key function:

    >>> # locale-aware sort order
    >>> sorted(iterable, key=cmp_to_key(locale.strcoll))
    

    有关排序示例和简短的排序教程,请参阅 排序的技术 教程。

    (由 Raymond Hettinger 贡献。)

itertools

  • itertools 模块有一个新的 accumulate() 函数,该函数模仿 APL 的 scan 运算符和 Numpy 的 accumulate 函数:

    >>> from itertools import accumulate
    >>> list(accumulate([8, 2, 50]))
    [8, 10, 60]
    
    >>> prob_dist = [0.1, 0.4, 0.2, 0.3]
    >>> list(accumulate(prob_dist))      # cumulative probability distribution
    [0.1, 0.5, 0.7, 1.0]
    

    有关使用 accumulate() 的示例,请参阅 random 模块的示例

    (由 Raymond Hettinger 贡献,并融合了 Mark Dickinson 的设计建议。)

collections

  • collections.Counter 类现在支持两种形式的原地减法操作:现有的 -= 运算符实现的是 饱和减法 (即结果不会低于零),而新增的 subtract() 方法则实现常规减法 (允许结果为负值)。前者适用于 多重集 (仅包含正计数的场景),后者更适合允许负计数的使用场景。

    >>> from collections import Counter
    >>> tally = Counter(dogs=5, cats=3)
    >>> tally -= Counter(dogs=2, cats=8)    # saturating subtraction
    >>> tally
    Counter({'dogs': 3})
    
    >>> tally = Counter(dogs=5, cats=3)
    >>> tally.subtract(dogs=2, cats=8)      # regular subtraction
    >>> tally
    Counter({'dogs': 3, 'cats': -5})
    

    (由 Raymond Hettinger 贡献。)

  • collections.OrderedDict 类有一个新方法 move_to_end(),它接受一个现有的键并将其移动到有序序列的第一个或最后一个位置。

    默认情况下,将项目移动到最后一个位置。这相当于用 od[k] = od.pop(k) 更新一个条目。

    快速移动到末尾的操作对于重新排序条目很有用。例如,有序字典可以用来跟踪访问顺序,通过将条目从最旧的更新到最近访问的。

    >>> from collections import OrderedDict
    >>> d = OrderedDict.fromkeys(['a', 'b', 'X', 'd', 'e'])
    >>> list(d)
    ['a', 'b', 'X', 'd', 'e']
    >>> d.move_to_end('X')
    >>> list(d)
    ['a', 'b', 'd', 'e', 'X']
    

    (由 Raymond Hettinger 贡献。)

  • collections.deque 类新增了两个方法 count()reverse(),使它们更易于替代 list 对象:

    >>> from collections import deque
    >>> d = deque('simsalabim')
    >>> d.count('s')
    2
    >>> d.reverse()
    >>> d
    deque(['m', 'i', 'b', 'a', 'l', 'a', 's', 'm', 'i', 's'])
    

    (由 Raymond Hettinger 贡献。)

threading

threading 模块有一个新的 Barrier 同步类,用于使多个线程等待,直到它们都达到一个共同的屏障点。屏障对于确保具有多个先决条件的任务在所有前置任务完成之前不运行非常有用。

屏障可以与任意数量的线程一起工作。这是对仅适用于两个线程的 会合点 的泛化。

作为两阶段循环屏障实现,Barrier 对象适用于循环中使用。独立的 填充排空 阶段确保所有线程在任何一个线程循环回并重新进入屏障之前都被释放(排空)。屏障在每个周期后完全重置。

使用屏障的示例:

from threading import Barrier, Thread

def get_votes(site):
    ballots = conduct_election(site)
    all_polls_closed.wait()        # 在所有投票站关闭之前不要计数
    totals = summarize(ballots)
    publish(site, totals)

all_polls_closed = Barrier(len(sites))
for site in sites:
    Thread(target=get_votes, args=(site,)).start()

在这个示例中,屏障强制执行一个规则,即在所有投票站关闭之前,任何投票站都不能计票。请注意,使用屏障的解决方案与使用 threading.Thread.join() 的解决方案类似,但线程在越过屏障点后仍然保持活动状态并继续工作(汇总选票)。

如果任何前置任务可能会挂起或延迟,可以创建一个带有可选 timeout 参数的屏障。然后,如果在超时期限内所有前置任务都没有到达屏障点,所有等待的线程将被释放,并引发 BrokenBarrierError 异常:

def get_votes(site):
    ballots = conduct_election(site)
    try:
        all_polls_closed.wait(timeout=midnight - time.now())
    except BrokenBarrierError:
        lockbox = seal_ballots(ballots)
        queue.put(lockbox)
    else:
        totals = summarize(ballots)
        publish(site, totals)

在这个示例中,屏障执行了一个更健壮的规则。如果某些选举站在午夜前没有完成,屏障将超时,选票将被封存并放入队列中以供后续处理。

请参阅 屏障同步模式 了解更多关于如何在并行计算中使用屏障的示例。此外,在 信号量小册子第3.6节 中也有一个简单但详尽的屏障解释。

(由 Kristján Valur Jónsson 贡献,并由 Jeffrey Yasskin 在 bpo-8777 中进行 API 审查。)

datetime 和 time

  • datetime 模块新增了一种类型 timezone,它通过返回固定的 UTC 偏移量和时区名称来实现 tzinfo 接口。这使得创建带有时区信息的 datetime 对象变得更加容易:

    >>> from datetime import datetime, timezone
    
    >>> datetime.now(timezone.utc)
    datetime.datetime(2010, 12, 8, 21, 4, 2, 923754, tzinfo=datetime.timezone.utc)
    
    >>> datetime.strptime("01/01/2000 12:00 +0000", "%m/%d/%Y %H:%M %z")
    datetime.datetime(2000, 1, 1, 12, 0, tzinfo=datetime.timezone.utc)
    
  • 此外,timedelta 对象现在可以与 float 相乘,并且可以被 floatint 对象除。同时,timedelta 对象现在可以相互除。

  • datetime.date.strftime() 方法不再限制于 1900 年之后的年份。新的支持年份范围是从 1000 年到 9999 年(含)。

  • 每当在时间元组中使用两位数年份时,其解释由 time.accept2dyear 控制。默认值为 True,这意味着对于两位数年份,世纪是根据 POSIX 规则(管理 %y strptime 格式)进行猜测的。

    从 Py3.2 开始,使用世纪猜测启发式方法将发出 DeprecationWarning 警告。建议将 time.accept2dyear 设置为 False,以便在没有猜测的情况下使用大的日期范围:

    >>> import time, warnings
    >>> warnings.resetwarnings()      # 移除默认的警告过滤器
    
    >>> time.accept2dyear = True      # 猜测 11 是指 11 还是 2011
    >>> time.asctime((11, 1, 1, 12, 34, 56, 4, 1, 0))
    Warning (from warnings module):
      ...
    DeprecationWarning: Century info guessed for a 2-digit year.
    'Fri Jan  1 12:34:56 2011'
    
    >>> time.accept2dyear = False     # 使用允许的完整日期范围
    >>> time.asctime((11, 1, 1, 12, 34, 56, 4, 1, 0))
    'Fri Jan  1 12:34:56 11'
    

    多个函数的日期范围已显著扩展。当 time.accept2dyear 为假时,time.asctime() 函数将接受任何适合 C int 类型的年份,而 time.mktime()time.strftime() 函数将接受相应操作系统函数支持的全部范围。

(由 Alexander Belopolsky 和 Victor Stinner 在 bpo-1289118, bpo-5094, bpo-6641, bpo-2706, bpo-1777412, bpo-8013bpo-10827 中贡献。)

math

math 模块基于 C99 标准增加了六个新函数。

isfinite() 函数提供了一种可靠且快速的方法来检测特殊值。它对常规数字返回 True,对 NanInfinity 返回 False

>>> from math import isfinite
>>> [isfinite(x) for x in (123, 4.56, float('Nan'), float('Inf'))]
[True, True, False, False]

expm1() 函数用于计算 e**x - 1 (其中 x 为较小数值),且不会产生因相减两个近似相等数值而导致精度损失的问题。

>>> from math import expm1
>>> expm1(0.013671875)   # more accurate way to compute e**x-1 for a small x
0.013765762467652909

erf() 函数计算概率积分或 高斯误差函数。其互补误差函数 erfc() 的计算公式为 1 - erf(x)

>>> from math import erf, erfc, sqrt
>>> erf(1.0/sqrt(2.0))   # 在 1 个标准差内的正态分布部分
0.682689492137086
>>> erfc(1.0/sqrt(2.0))  # 在 1 个标准差外的正态分布部分
0.31731050786291404
>>> erf(1.0/sqrt(2.0)) + erfc(1.0/sqrt(2.0))
1.0

gamma() 函数是阶乘函数的连续扩展。详情请见 https://en.wikipedia.org/wiki/Gamma_function。由于该函数与阶乘相关,即使对于小的 x 值,其值也会变得很大,因此还有一个 lgamma() 函数用于计算伽马函数的自然对数:

>>> from math import gamma, lgamma
>>> gamma(7.0)           # six factorial
720.0
>>> lgamma(801.0)        # log(800 factorial)
4551.950730698041

(由 Mark Dickinson 贡献)

abc

abc 模块现在支持 abstractclassmethod()abstractstaticmethod()

这些工具使得可以定义一个要求实现特定 classmethod()staticmethod()抽象基类

class Temperature(metaclass=abc.ABCMeta):
    @abc.abstractclassmethod
    def from_fahrenheit(cls, t):
        ...
    @abc.abstractclassmethod
    def from_celsius(cls, t):
        ...

(补丁由 Daniel Urban 在 bpo-5867 中提交。)

io

io.BytesIO 类新增了一个方法 getbuffer(),其功能类似于 memoryview()。该方法会创建一个数据的可编辑视图,且不会生成数据的副本。该缓冲区支持随机访问和切片表示法,非常适合进行原地编辑操作:

>>> REC_LEN, LOC_START, LOC_LEN = 34, 7, 11

>>> def change_location(buffer, record_number, location):
...     start = record_number * REC_LEN + LOC_START
...     buffer[start: start+LOC_LEN] = location

>>> import io

>>> byte_stream = io.BytesIO(
...     b'G3805  storeroom  Main chassis    '
...     b'X7899  shipping   Reserve cog     '
...     b'L6988  receiving  Primary sprocket'
... )
>>> buffer = byte_stream.getbuffer()
>>> change_location(buffer, 1, b'warehouse  ')
>>> change_location(buffer, 0, b'showroom   ')
>>> print(byte_stream.getvalue())
b'G3805  showroom   Main chassis    '
b'X7899  warehouse  Reserve cog     '
b'L6988  receiving  Primary sprocket'

(由 Antoine Pitrou 在 bpo-5506 中贡献。)

reprlib

在为自定义容器编写 __repr__() 方法时,很容易忘记处理成员引用容器自身的情况。Python的内置对象如 listset 通过在表示字符串的递归部分显示 "..." 来处理自引用。

为了帮助编写此类 __repr__() 方法,reprlib 模块新增了一个装饰器 recursive_repr(),用于检测对 __repr__() 的递归调用并替换为占位符字符串:

>>> class MyList(list):
...     @recursive_repr()
...     def __repr__(self):
...         return '<' + '|'.join(map(repr, self)) + '>'
...
>>> m = MyList('abc')
>>> m.append(m)
>>> m.append('x')
>>> print(m)
<'a'|'b'|'c'|...|'x'>

(由 Raymond Hettinger 在 bpo-9826bpo-9840 中贡献。)

logging

除了上述基于字典的配置外,logging 包还有许多其他改进。

日志模块的文档已新增一份 基础教程、一份 高级教程 以及一份包含日志实践案例的 指南。这些文档是学习日志功能的最佳途径。

logging.basicConfig() 配置函数新增了一个 style 参数,用于支持三种不同的字符串格式化方式。该参数默认值为 "%",表示传统的 %-格式化;可设置为 "{",采用新的 str.format() 格式化风格;也可设置为 "$",使用 string.Template 提供的类 shell 风格格式化。以下三种配置方式是等效的:

>>> from logging import basicConfig
>>> basicConfig(style='%', format="%(name)s -> %(levelname)s: %(message)s")
>>> basicConfig(style='{', format="{name} -> {levelname} {message}")
>>> basicConfig(style='$', format="$name -> $levelname: $message")

如果在日志事件发生之前没有进行配置,现在将使用默认配置,该配置使用 StreamHandlerWARNING 级别或更高的事件定向到 sys.stderr。以前,在配置设置之前发生的事件要么抛出异常,要么根据 logging.raiseExceptions 的值静默丢弃事件。新的默认处理程序存储在 logging.lastResort 中。

过滤器的使用已经简化。现在不再需要创建 Filter 对象,谓词可以是任何返回 TrueFalse 的 Python 可调用对象。

还有许多其他改进,增加了灵活性并简化了配置。请参阅模块文档,了解 Python 3.2 中所有更改的完整列表。

csv

csv 模块现在支持一个新的方言,unix_dialect,它对所有字段应用引号,并使用传统的 Unix 风格,以 '\n' 作为行终止符。注册的方言名称为 unix

csv.DictWriter 有一个新的 writeheader() 方法,用于写入初始行以记录字段名称:

>>> import csv, sys
>>> w = csv.DictWriter(sys.stdout, ['name', 'dept'], dialect='unix')
>>> w.writeheader()
"name","dept"
>>> w.writerows([
...     {'name': 'tom', 'dept': 'accounting'},
...     {'name': 'susan', 'dept': 'Salesl'}])
"tom","accounting"
"susan","sales"

(新方言由 Jay Talbot 在 bpo-5975 中建议,新方法由 Ed Abraham 在 bpo-1537721 中建议。)

contextlib

有一个新的且略显震撼的工具 ContextDecorator,它有助于创建一个既可以作为函数装饰器又可以作为 上下文管理器 的双重用途的上下文管理器。

为了方便起见,这一新功能被 contextmanager() 使用,因此无需额外努力即可支持这两种角色。

基本思想是,上下文管理器和函数装饰器都可以用于前置动作和后置动作的包装器。上下文管理器使用 with 语句包装一组语句,而函数装饰器包装一个函数内的一组语句。因此,有时需要编写一个可以在任一角色中使用的前置动作或后置动作包装器。

例如,有时需要用一个日志记录器包装函数或一组语句,以跟踪进入和退出时间。与其为该任务编写一个函数装饰器和一个上下文管理器,不如使用 contextmanager() 在单一定义中提供这两种能力:

from contextlib import contextmanager
import logging

logging.basicConfig(level=logging.INFO)

@contextmanager
def track_entry_and_exit(name):
    logging.info('Entering: %s', name)
    yield
    logging.info('Exiting: %s', name)

以前,这只能用作上下文管理器:

with track_entry_and_exit('widget loader'):
    print('Some time consuming activity goes here')
    load_widget()

现在,它也可以用作装饰器:

@track_entry_and_exit('widget loader')
def activity():
    print('Some time consuming activity goes here')
    load_widget()

试图同时满足两种角色对技术有一些限制。上下文管理器通常具有返回一个可被 with 语句使用的参数的灵活性,但函数装饰器没有类似的特性。

在上面的示例中,track_entry_and_exit 上下文管理器没有一种简洁的方式来返回一个日志实例,以供封闭语句体中使用。

(由 Michael Foord 在 bpo-9110 中贡献。)

decimal 和 fractions

Mark Dickinson 设计了一个优雅且高效的方案,确保不同数值数据类型在实际值相等时具有相同的哈希值(bpo-8188):

assert hash(Fraction(3, 2)) == hash(1.5) == \
       hash(Decimal("1.5")) == hash(complex(1.5, 0))

一些哈希细节通过新的属性 sys.hash_info 公开,该属性描述了哈希值的位宽、素数模数、infinitynan 的哈希值,以及用于数字虚部的乘数:

>>> sys.hash_info
sys.hash_info(width=64, modulus=2305843009213693951, inf=314159, nan=0, imag=1000003)

早期限制各种数值类型互操作性的决定已经放宽。仍然不支持(也不建议)在算术表达式中隐式混合使用,例如 Decimal('1.1') + float('1.1'),因为后者在构建二进制浮点数的过程中会丢失信息。然而,由于现有的浮点值可以无损地转换为十进制或分数表示,因此将其添加到构造函数并支持混合类型比较是有意义的。

fractions.Fraction 也进行了类似的更改,因此不再需要 from_float()from_decimal() 方法(bpo-8294):

>>> from decimal import Decimal
>>> from fractions import Fraction
>>> Decimal(1.1)
Decimal('1.100000000000000088817841970012523233890533447265625')
>>> Fraction(1.1)
Fraction(2476979795053773, 2251799813685248)

decimal 模块的一个有用改动是,Context.clamp 属性现在变为公开。这在创建与 IEEE 754 指定的十进制交换格式相对应的上下文时非常有用(参见 bpo-8540)。

(由 Mark Dickinson 和 Raymond Hettinger贡献。)

ftp

ftplib.FTP 类现在支持上下文管理协议,可以无条件地捕获 socket.error 异常,并在完成操作时关闭 FTP 连接:

>>> from ftplib import FTP
>>> with FTP("ftp1.at.proftpd.org") as ftp:
        ftp.login()
        ftp.dir()

'230 Anonymous login ok, restrictions apply.'
dr-xr-xr-x   9 ftp      ftp           154 May  6 10:43 .
dr-xr-xr-x   9 ftp      ftp           154 May  6 10:43 ..
dr-xr-xr-x   5 ftp      ftp          4096 May  6 10:43 CentOS
dr-xr-xr-x   3 ftp      ftp            18 Jul 10  2008 Fedora

其他文件型对象如 mmap.mmapfileinput.input() 也有了支持自动关闭的上下文管理器:

with fileinput.input(files=('log1.txt', 'log2.txt')) as f:
    for line in f:
        process(line)

(由 Tarek Ziadé 和 Giampaolo Rodolà 在 bpo-4972 贡献,由 Georg Brandl 在 bpo-8046bpo-1286 贡献。)

FTP_TLS 类现在接受一个 context 参数,这是一个 ssl.SSLContext 对象,允许将 SSL 配置选项、证书和私钥捆绑到一个单一的(可能是长寿命的)结构体中。

(由 Giampaolo Rodolà 在 bpo-8806 中贡献。)

popen

os.popen()subprocess.Popen() 函数现在支持使用 with 语句来自动关闭文件描述符。

(由 Antoine Pitrou 和 Brian Curtin 在 bpo-7461bpo-10554 中贡献。)

select

select 模块现在暴露了一个新的常量属性,PIPE_BUF,它给出了在 select.select() 表示管道准备好写入时,保证不会阻塞的最小字节数。

>>> import select
>>> select.PIPE_BUF
512

(在 Unix 系统上可用。 由 Sébastien Sablé 在 bpo-9862 中提供补丁)

gzip 和 zipfile

gzip.GzipFile 现在实现了 io.BufferedIOBase 抽象基类 (除了 truncate())。它还有一个 peek() 方法,并支持不可 seek 的以及零填充的文件对象。

gzip 模块还增加了 compress()decompress() 函数,以便更容易地进行内存中的压缩和解压缩。请记住,文本在压缩和解压缩之前需要编码为 bytes

>>> import gzip
>>> s = 'Three shall be the number thou shalt count, '
>>> s += 'and the number of the counting shall be three'
>>> b = s.encode()                        # convert to utf-8
>>> len(b)
89
>>> c = gzip.compress(b)
>>> len(c)
77
>>> gzip.decompress(c).decode()[:42]      # decompress and convert to text
'Three shall be the number thou shalt count'

(由 Anand B. Pillai 在 bpo-3488 中贡献,由Antoine Pitrou, Nir Aides 和 Brian Curtin 在 bpo-9962bpo-1675951bpo-7471bpo-2846 中贡献。)

此外,zipfile.ZipExtFile 类在内部进行了重新设计,以表示存储在存档中的文件。新的实现显著更快,并且可以包装在 io.BufferedReader 对象中以进一步提升速度。它还解决了交替调用 readreadline 导致结果错误的问题。

(补丁由 Nir Aides 在 bpo-7610 中提交。)

tarfile

TarFile 类现在可以用作上下文管理器。此外,其 add() 方法有一个新的选项 filter,它控制哪些文件被添加到存档中,并允许编辑文件元数据。

新的 filter 选项取代了旧的、灵活性较差的 exclude 参数,后者现已弃用。如果指定,可选的 filter 参数需要是一个 关键字参数。用户提供的过滤函数接受一个 TarInfo 对象,并返回一个更新的 TarInfo 对象,如果希望排除该文件,函数可以返回 None:

>>> import tarfile, glob

>>> def myfilter(tarinfo):
...     if tarinfo.isfile():             # 仅保存真实文件
...         tarinfo.uname = 'monty'      # 涂改用户名
...         return tarinfo

>>> with tarfile.open(name='myarchive.tar.gz', mode='w:gz') as tf:
...     for filename in glob.glob('*.txt'):
...         tf.add(filename, filter=myfilter)
...     tf.list()
-rw-r--r-- monty/501        902 2011-01-26 17:59:11 annotations.txt
-rw-r--r-- monty/501        123 2011-01-26 17:59:11 general_questions.txt
-rw-r--r-- monty/501       3514 2011-01-26 17:59:11 prion.txt
-rw-r--r-- monty/501        124 2011-01-26 17:59:11 py_todo.txt
-rw-r--r-- monty/501       1399 2011-01-26 17:59:11 semaphore_notes.txt

(由 Tarek Ziadé 提议并由 Lars Gustäbel 在 bpo-6856 中实现。)

hashlib

hashlib 模块有两个新的常量属性,列出了在所有实现中保证存在的哈希算法和当前实现中可用的算法:

>>> import hashlib

>>> hashlib.algorithms_guaranteed
{'sha1', 'sha224', 'sha384', 'sha256', 'sha512', 'md5'}

>>> hashlib.algorithms_available
{'md2', 'SHA256', 'SHA512', 'dsaWithSHA', 'mdc2', 'SHA224', 'MD4', 'sha256',
'sha512', 'ripemd160', 'SHA1', 'MDC2', 'SHA', 'SHA384', 'MD2',
'ecdsa-with-SHA1','md4', 'md5', 'sha1', 'DSA-SHA', 'sha224',
'dsaEncryption', 'DSA', 'RIPEMD160', 'sha', 'MD5', 'sha384'}

(由 Carl Chenet 在 bpo-7418 中建议。)

ast

ast 模块提供了一个用于安全评估表达式字符串的通用工具,使用 Python 字面量语法。ast.literal_eval() 函数作为内置 eval() 函数的安全替代,后者容易被滥用。Python 3.2 将 bytesset 字面量添加到支持的类型列表中:字符串、字节、数字、元组、列表、字典、集合、布尔值和 None

>>> from ast import literal_eval

>>> request = "{'req': 3, 'func': 'pow', 'args': (2, 0.5)}"
>>> literal_eval(request)
{'args': (2, 0.5), 'req': 3, 'func': 'pow'}

>>> request = "os.system('do something harmful')"
>>> literal_eval(request)
Traceback (most recent call last):
  ...
ValueError: malformed node or string: <_ast.Call object at 0x101739a10>

(由Benjamin Peterson 和 Georg Brandl 实现。)

os

不同的操作系统对文件名和环境变量使用各种编码。os 模块提供了两个新函数,fsencode()fsdecode(),用于编码和解码文件名:

>>> import os
>>> filename = 'Sehenswürdigkeiten'
>>> os.fsencode(filename)
b'Sehensw\xc3\xbcrdigkeiten'

某些操作系统允许直接访问环境中的编码字节。如果是这样,os.supports_bytes_environ 常量将为真。

为了直接访问编码的环境变量(如果可用),使用新的 os.getenvb() 函数,或者使用 os.environb,它是 os.environ 的字节版本。

(由 Victor Stinner 贡献。)

shutil

shutil.copytree() 函数增加了两个新选项:

  • ignore_dangling_symlinks:当 symlinks=False 时,函数会复制由符号链接指向的文件,而不是符号链接本身。此选项将静默处理文件不存在时引发的错误。

  • copy_function:是一个用于复制文件的可调用对象。默认使用 shutil.copy2()

(由 Tarek Ziadé 贡献。)

此外,shutil 模块现在支持对 zip 文件、未压缩的 tar 文件、gzip 压缩的 tar 文件和 bzip 压缩的 tar 文件的 归档操作。还有用于注册其他归档文件格式(如 xz 压缩的 tar 文件或自定义格式)的函数。

主要函数是 make_archive()unpack_archive()。默认情况下,两者操作当前目录(可以通过 os.chdir() 设置)及其任何子目录。归档文件名需要指定完整路径名。归档步骤是非破坏性的(原始文件保持不变)。

>>> import shutil, pprint

>>> os.chdir('mydata')  # 改至源目录
>>> f = shutil.make_archive('/var/backup/mydata',
...                         'zip')      # 将当前目录归档
>>> f                                   # 显示归档文件名
'/var/backup/mydata.zip'
>>> os.chdir('tmp')                     # 改至解包目录
>>> shutil.unpack_archive('/var/backup/mydata.zip')  # 恢复数据

>>> pprint.pprint(shutil.get_archive_formats())  # 显示已知格式
[('bztar', "bzip2'ed tar-file"),
 ('gztar', "gzip'ed tar-file"),
 ('tar', 'uncompressed tar file'),
 ('zip', 'ZIP file')]

>>> shutil.register_archive_format(     # 注册新的归档格式
...     name='xz',
...     function=xz.compress,           # 可调用的归档函数
...     extra_args=[('level', 8)],      # 函数的参数
...     description='xz compression'
... )

(由 Tarek Ziadé 贡献。)

sqlite3

sqlite3 模块被更新至 pysqlite 2.6.0 版。 它拥有两个新功能。

(由 R. David Murray 和 Shashwat Anand 在 bpo-8845 中贡献。)

html

引入了一个新的 html 模块,其中只有一个函数 escape(),用于转义 HTML 标记中的保留字符:

>>> import html
>>> html.escape('x > 2 && x < 7')
'x &gt; 2 &amp;&amp; x &lt; 7'

socket

socket 模块有两项新改进。

  • Socket对象现在有一个 detach() 方法,该方法将套接字置于关闭状态,而不实际关闭底层的文件描述符。后者随后可以用于其他目的。(由 Antoine Pitrou 在 bpo-8524 中添加。)

  • socket.create_connection() 现在支持上下文管理协议,以无条件消耗 socket.error 异常,并在完成后关闭套接字。(由 Giampaolo Rodolà 在 bpo-9794 中贡献。)

ssl

ssl 模块添加了许多功能以满足安全(加密、认证)互联网连接的常见需求:

  • 一个新的类,SSLContext,用作持久SSL数据的容器,例如协议设置、证书、私钥和各种其他选项。它包括一个 wrap_socket() 方法,用于从SSL上下文创建SSL套接字。

  • 一个新的函数,ssl.match_hostname(),通过实现HTTPS(来自 RFC 2818)的规则来支持高级协议的服务器身份验证,这些规则也适用于其他协议。

  • ssl.wrap_socket() 构造函数现在接受一个 ciphers 参数。ciphers 字符串列出了允许的加密算法,使用 OpenSSL文档 中描述的格式。

  • 当与较新版本的 OpenSSL 库链接时,ssl 模块现在支持 TLS 协议的服务器名称指示(Server Name Indication,SNI)扩展。该特性允许在单个 IP 端口上为多个使用不同证书的“虚拟主机”提供服务。需要注意的是,此扩展仅在客户端模式下受支持,并且需要通过向 ssl.SSLContext.wrap_socket() 方法传递 server_hostname 参数来激活。

  • 已向 ssl 模块添加了各种选项,例如 OP_NO_SSLv2,该选项禁用了不安全且已过时的SSLv2协议。

  • 扩展现在加载了所有OpenSSL密码和摘要算法。如果某些SSL证书无法验证,它们将被报告为“未知算法”错误。

  • 现在可以通过模块属性 ssl.OPENSSL_VERSION (字符串) 、ssl.OPENSSL_VERSION_INFO (一个5元组) 和 ssl.OPENSSL_VERSION_NUMBER (整数) 访问正在使用的OpenSSL版本。

(由 Antoine Pitrou 在 bpo-8850, bpo-1589, bpo-8322, bpo-5639, bpo-4870, bpo-8484bpo-8321 中贡献。)

nntp

nntplib 模块进行了重新实现,改进了字节和文本语义以及更实用的API。这些改进破坏了与Python 3.1中nntplib版本的兼容性,后者本身部分功能不完善。

还增加了通过隐式(使用 nntplib.NNTP_SSL)和显式(使用 nntplib.NNTP.starttls())TLS进行安全连接的支持。

(由 Antoine Pitrou 在 bpo-9360 中贡献,由 Andrew Vant 在 bpo-1926 中贡献。)

certificates

http.client.HTTPSConnectionurllib.request.HTTPSHandlerurllib.request.urlopen() 现在接受可选参数,以允许对服务器证书进行检查,以验证一组证书颁发机构,这是公共HTTPS使用的推荐做法。

(由 Antoine Pitrou 添加,bpo-9003。)

imaplib

通过新的 imaplib.IMAP4.starttls 方法,标准IMAP4连接上增加了显式TLS支持。

(由 Lorenzo M. Catucci 和 Antoine Pitrou 在 bpo-4471 中贡献。)

http.client

http.client 模块中进行了一些小的API改进。不再支持旧式HTTP 0.9简单响应,所有类中的 strict 参数已被弃用。

HTTPConnectionHTTPSConnection 类现在有一个 source_address 参数,用于指示(主机,端口)元组,表明HTTP连接是从哪里建立的。

HTTPSConnection 增加了证书检查和HTTPS虚拟主机的支持。

连接对象上的 request() 方法允许一个可选的 body 参数,以便可以使用 文件对象 来提供请求的内容。方便的是,body 参数现在也接受一个 可迭代 对象,只要它包含一个明确的 Content-Length 头部。这个扩展接口比以前更加灵活。

为了通过代理服务器建立 HTTPS 连接,新增了一个 set_tunnel() 方法,用于设置 HTTP Connect 隧道的主机和端口。

为了与 http.server 的行为保持一致,HTTP 客户端库现在也使用 ISO-8859-1(Latin-1)编码对头部进行编码。它已经对传入的头部这样做了,所以现在传入和传出流量的行为是一致的。(参见 Armin Ronacher 在 bpo-10980 中的工作。)

unittest

unittest 模块有许多改进,支持包的测试发现、在交互式提示符下更容易进行实验、新的测试用例方法、改进的测试失败诊断消息以及更好的方法名称。

  • 命令行调用 python -m unittest 现在接受文件路径而不是模块名来运行特定的测试 (bpo-10620)。新的测试发现可以在包内找到测试,定位从顶级目录可导入的任何测试。顶级目录可以用 -t 选项指定,用 -p 匹配文件的模式,以及用 -s 指定开始发现的目录:

    $ python -m unittest discover -s my_proj_dir -p _test.py
    

    (由 Michael Foord 贡献)

  • 在交互式提示符下进行实验现在更容易了,因为 unittest.TestCase 类现在可以不带参数实例化:

    >>> from unittest import TestCase
    >>> TestCase().assertEqual(pow(2, 3), 8)
    

    (由 Michael Foord 贡献)

  • unittest 模块有两个新方法,assertWarns()assertWarnsRegex(),用于验证给定警告类型是否由被测试代码触发:

    with self.assertWarns(DeprecationWarning):
        legacy_function('XYZ')
    

    (由 Antoine Pitrou 在 bpo-9754 中贡献。)

    另一个新方法,assertCountEqual() 用于比较两个可迭代对象,以确定它们的元素计数是否相等(无论顺序如何,相同元素的出现次数是否相同):

    def test_anagram(self):
        self.assertCountEqual('algorithm', 'logarithm')
    

    (由 Raymond Hettinger 贡献。)

  • unittest 模块的主要特性之一是当测试失败时,努力生成有意义的诊断信息。在可能的情况下,失败记录会附带输出内容的差异。这对于分析失败的测试运行的日志文件特别有帮助。然而,由于差异有时可能非常庞大,因此有一个新的 maxDiff 属性用于设置显示的差异最大长度。

  • 此外,模块中的方法名称已经进行了多项清理。

    例如,assertRegex()assertRegexpMatches() 的新名称,后者命名不当,因为测试使用的是 re.search(),而不是 re.match()。其他使用正则表达式的方法现在也优先使用短形式 "Regex" 而不是 "Regexp" —— 这与其他 unittest 实现中的命名一致,匹配 Python 中 re 模块的旧名称,并且具有明确的驼峰命名。

    (由 Raymond Hettinger 贡献并由 Ezio Melotti 实现。)

  • 为了提高一致性,一些长期存在的方法别名正在被弃用,转而使用首选名称:

    旧名称

    首选名称

    assert_()

    assertTrue()

    assertEquals()

    assertEqual()

    assertNotEquals()

    assertNotEqual()

    assertAlmostEquals()

    assertAlmostEqual()

    assertNotAlmostEquals()

    assertNotAlmostEqual()

    同样,Python 3.1 中弃用的 TestCase.fail* 方法预计将在 Python 3.3 中被移除。

    (由 Ezio Melotti 在 bpo-9424 中贡献。)

  • assertDictContainsSubset() 方法被弃用,因为它的实现有误,参数顺序错误。这导致了难以调试的视觉错觉,例如 TestCase().assertDictContainsSubset({'a':1, 'b':2}, {'a':1}) 这样的测试会失败。

    (由 Raymond Hettinger 贡献。)

random

random 模块中的整数方法现在能更好地生成均匀分布。之前,它们使用 int(n*random()) 计算选择,这在 n 不是二的幂时存在轻微偏差。现在,从下一个二的幂的范围中进行多次选择,并且只有当选择落在 0 <= x < n 范围内时才保留。受影响的函数和方法包括 randrange()randint()choice()shuffle()sample()

(由 Raymond Hettinger 在 bpo-9025 中贡献。)

poplib

POP3_SSL 类现在接受一个 context 参数,这是一个 ssl.SSLContext 对象,允许将 SSL 配置选项、证书和私钥捆绑到一个单一的(可能是长期存在的)结构体中。

(由 Giampaolo Rodolà 在 bpo-8807 中贡献。)

asyncore

asyncore.dispatcher 现在提供了一个 handle_accepted() 方法,返回一个 (sock, addr) 对,当与新远程端点实际建立连接时调用。这应作为旧 handle_accept() 的替代,避免了用户直接调用 accept()

(由 Giampaolo Rodolà 在 bpo-6706 中贡献。)

tempfile

tempfile 模块有一个新的上下文管理器,TemporaryDirectory,它提供了临时目录的简单确定性清理:

with tempfile.TemporaryDirectory() as tmpdirname:
    print('创建的临时目录:', tmpdirname)

(由 Neil Schemenauer 和 Nick Coghlan 在 bpo-5178 中贡献。)

inspect

  • inspect 模块有一个新函数 getgeneratorstate() 用来方便地标识一个生成器迭代器的当前状态:

    >>> from inspect import getgeneratorstate
    >>> def gen():
    ...     yield 'demo'
    ...
    >>> g = gen()
    >>> getgeneratorstate(g)
    'GEN_CREATED'
    >>> next(g)
    'demo'
    >>> getgeneratorstate(g)
    'GEN_SUSPENDED'
    >>> next(g, None)
    >>> getgeneratorstate(g)
    'GEN_CLOSED'
    

    (由 Rodolpho Eckhardt 和 Nick Coghlan 在 bpo-10220 中贡献。)

  • 为了支持查找而不激活动态属性,inspect 模块有一个新函数,getattr_static()。与 hasattr() 不同,这是一个真正的只读搜索,保证在搜索过程中不会改变状态:

    >>> class A:
    ...     @property
    ...     def f(self):
    ...         print('运行中')
    ...         return 10
    ...
    >>> a = A()
    >>> getattr(a, 'f')
    运行中
    10
    >>> inspect.getattr_static(a, 'f')
    <property object at 0x1022bd788>
    

(由 Michael Foord 贡献)

pydoc

pydoc 模块现在提供了一个大幅改进的Web服务器接口,以及一个新的命令行选项 -b,用于自动打开浏览器窗口以显示该服务器:

$ pydoc3.2 -b

(由 Ron Adam 在 bpo-2001 中贡献。)

dis

dis 模块新增了两个用于检查代码的函数,code_info()show_code()。两者都为提供的函数、方法、源代码字符串或代码对象提供详细的代码对象信息。前者返回一个字符串,后者将其打印出来:

>>> import dis, random
>>> dis.show_code(random.choice)
Name:              choice
Filename:          /Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/random.py
Argument count:    2
Kw-only arguments: 0
Number of locals:  3
Stack size:        11
Flags:             OPTIMIZED, NEWLOCALS, NOFREE
Constants:
   0: 'Choose a random element from a non-empty sequence.'
   1: 'Cannot choose from an empty sequence'
Names:
   0: _randbelow
   1: len
   2: ValueError
   3: IndexError
Variable names:
   0: self
   1: seq
   2: i

此外,dis() 函数现在接受字符串参数,以便常用的 dis(compile(s, '', 'eval')) 可以简化为 dis(s)

>>> dis('3*x+1 if x%2==1 else x//2')
  1           0 LOAD_NAME                0 (x)
              3 LOAD_CONST               0 (2)
              6 BINARY_MODULO
              7 LOAD_CONST               1 (1)
             10 COMPARE_OP               2 (==)
             13 POP_JUMP_IF_FALSE       28
             16 LOAD_CONST               2 (3)
             19 LOAD_NAME                0 (x)
             22 BINARY_MULTIPLY
             23 LOAD_CONST               1 (1)
             26 BINARY_ADD
             27 RETURN_VALUE
        >>   28 LOAD_NAME                0 (x)
             31 LOAD_CONST               0 (2)
             34 BINARY_FLOOR_DIVIDE
             35 RETURN_VALUE

综合来看,这些改进使得探索CPython的实现方式以及亲自查看语言语法在底层的作用变得更加容易。

(由 Nick Coghlan 在 bpo-9147 中贡献。)

dbm

所有数据库模块现在均支持 get()setdefault() 方法。

(由 Ray Allen 在 bpo-9523 中建议。)

ctypes

一个新类型 ctypes.c_ssize_t 用来表示 C ssize_t 数据类型。

site

site 模块新增了三个用于报告给定 Python 安装版详细信息的函数。

>>> import site
>>> site.getsitepackages()
['/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/site-packages',
 '/Library/Frameworks/Python.framework/Versions/3.2/lib/site-python',
 '/Library/Python/3.2/site-packages']
>>> site.getuserbase()
'/Users/raymondhettinger/Library/Python/3.2'
>>> site.getusersitepackages()
'/Users/raymondhettinger/Library/Python/3.2/lib/python/site-packages'

部分 site 功能可方便地直接通过命令行访问:

$ python -m site --user-base
/Users/raymondhettinger/.local
$ python -m site --user-site
/Users/raymondhettinger/.local/lib/python3.2/site-packages

(由 Tarek Ziadé 在 bpo-6693 中贡献。)

sysconfig

新增的 sysconfig 模块使得发现依赖于不同系统平台和安装版的安装路径和配置变量更为简单直观。

该模块提供了对平台和版本信息获取函数的访问:

它还提供了与 distutils 所使用的七个规范名称所对应的路径和变量的访问。 这包括 posix_prefix, posix_home, posix_user, nt, nt_user, os2, os2_home:

  • get_paths() 返回一个包含当前安装方案安装路径的字典。

  • get_config_vars() 返回一个包含平台特定变量的字典。

还有一个方便的命令行界面:

C:\Python32>python -m sysconfig
Platform: "win32"
Python version: "3.2"
Current installation scheme: "nt"

Paths:
        data = "C:\Python32"
        include = "C:\Python32\Include"
        platinclude = "C:\Python32\Include"
        platlib = "C:\Python32\Lib\site-packages"
        platstdlib = "C:\Python32\Lib"
        purelib = "C:\Python32\Lib\site-packages"
        scripts = "C:\Python32\Scripts"
        stdlib = "C:\Python32\Lib"

Variables:
        BINDIR = "C:\Python32"
        BINLIBDEST = "C:\Python32\Lib"
        EXE = ".exe"
        INCLUDEPY = "C:\Python32\Include"
        LIBDEST = "C:\Python32\Lib"
        SO = ".pyd"
        VERSION = "32"
        abiflags = ""
        base = "C:\Python32"
        exec_prefix = "C:\Python32"
        platbase = "C:\Python32"
        prefix = "C:\Python32"
        projectbase = "C:\Python32"
        py_version = "3.2"
        py_version_nodot = "32"
        py_version_short = "3.2"
        srcdir = "C:\Python32"
        userbase = "C:\Documents and Settings\Raymond\Application Data\Python"

(由TarekZiadé 移出Distutils。)

pdb

pdb 调试器模块获得了一些可用性改进:

  • pdb.py 现在有一个 -c 选项,用于执行 .pdbrc 脚本文件中给出的命令。

  • 一个 .pdbrc 脚本文件可以包含 continuenext 命令,用于继续调试。

  • Pdb 类构造函数现在接受一个 nosigint 参数。

  • 新命令:l(list)ll(long list)source 用于列出源代码。

  • 新命令:displayundisplay 用于显示或隐藏表达式值的变化。

  • 新命令:interact 用于启动一个包含当前作用域中全局和局部名称的交互式解释器。

  • 可以通过断点编号清除断点。

(由Georg Brandl, Antonio Cuni 和 Ilya Sandler 贡献。)

configparser

configparser 模块已被修改,以提高默认解析器的可用性和其支持的 INI 语法的可预测性。旧的 ConfigParser 类已被移除,改为 SafeConfigParser,后者已被重命名为 ConfigParser。内联注释支持现在默认关闭,并且单个配置源中不允许有节或选项重复。

配置解析器获得了一个基于映射协议的新 API:

>>> parser = ConfigParser()
>>> parser.read_string("""
... [DEFAULT]
... location = upper left
... visible = yes
... editable = no
... color = blue
...
... [main]
... title = Main Menu
... color = green
...
... [options]
... title = Options
... """)
>>> parser['main']['color']
'green'
>>> parser['main']['editable']
'no'
>>> section = parser['options']
>>> section['title']
'Options'
>>> section['title'] = 'Options (editable: %(editable)s)'
>>> section['title']
'Options (editable: no)'

新 API 是在经典 API 之上实现的,因此自定义解析器子类应能够无修改地使用它。

配置解析器接受的 INI 文件结构体现在可以自定义。用户可以指定替代的选项/值分隔符和注释前缀,更改 DEFAULT 部分的名称或切换插值语法。

支持可插拔插值,包括一个额外的插值处理程序 ExtendedInterpolation

>>> parser = ConfigParser(interpolation=ExtendedInterpolation())
>>> parser.read_dict({'buildout': {'directory': '/home/ambv/zope9'},
...                   'custom': {'prefix': '/usr/local'}})
>>> parser.read_string("""
... [buildout]
... parts =
...   zope9
...   instance
... find-links =
...   ${buildout:directory}/downloads/dist
...
... [zope9]
... recipe = plone.recipe.zope9install
... location = /opt/zope
...
... [instance]
... recipe = plone.recipe.zope9instance
... zope9-location = ${zope9:location}
... zope-conf = ${custom:prefix}/etc/zope.conf
... """)
>>> parser['buildout']['find-links']
'\n/home/ambv/zope9/downloads/dist'
>>> parser['instance']['zope-conf']
'/usr/local/etc/zope.conf'
>>> instance = parser['instance']
>>> instance['zope-conf']
'/usr/local/etc/zope.conf'
>>> instance['zope9-location']
'/opt/zope'

此外还引入了一些较小的功能特性,例如:支持在读取操作中指定编码格式、为获取函数(get-functions)指定回退值,以及直接从字典和字符串中读取数据。

(所有改变均由 Łukasz Langa 贡献。)

urllib.parse

urllib.parse 模块进行了一些可用性改进。

urlparse() 函数现在支持 IPv6 地址,如 RFC 2732 中所述:

>>> import urllib.parse
>>> urllib.parse.urlparse('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]/foo/')
ParseResult(scheme='http',
            netloc='[dead:beef:cafe:5417:affe:8FA3:deaf:feed]',
            path='/foo/',
            params='',
            query='',
            fragment='')

urldefrag() 函数现在返回一个 具名元组:

>>> r = urllib.parse.urldefrag('http://python.org/about/#target')
>>> r
DefragResult(url='http://python.org/about/', fragment='target')
>>> r[0]
'http://python.org/about/'
>>> r.fragment
'target'

此外,urlencode() 函数现在更加灵活,可以接受字符串或字节类型作为 query 参数。如果是字符串,那么 safeencodingerror 参数将被传递给 quote_plus() 进行编码:

>>> urllib.parse.urlencode([
...      ('type', 'telenovela'),
...      ('name', '¿Dónde Está Elisa?')],
...      encoding='latin-1')
'type=telenovela&name=%BFD%F3nde+Est%E1+Elisa%3F'

解析ASCII编码字节 中所述,所有 urllib.parse 函数现在都接受 ASCII 编码的字节字符串作为输入,只要它们不与普通字符串混合。如果给定 ASCII 编码的字节字符串作为参数,返回类型也将是 ASCII 编码的字节字符串:

>>> urllib.parse.urlparse(b'http://www.python.org:80/about/')
ParseResultBytes(scheme=b'http', netloc=b'www.python.org:80',
                 path=b'/about/', params=b'', query=b'', fragment=b'')

(由 Nick Coghlan、Dan Mahn 和 Senthil Kumaran 在 bpo-2987bpo-5468bpo-9873 中提供。)

mailbox

由于 R. David Murray 的共同努力,mailbox 模块已修复以适用于 Python 3.2。挑战在于 mailbox 最初是使用文本接口设计的,但电子邮件消息最好用 bytes 表示,因为消息的不同部分可能具有不同的编码。

解决方案利用了 email 包的二进制支持来解析任意电子邮件消息。此外,解决方案还需要进行一些 API 更改。

如预期的那样,mailbox.Mailbox 对象的 add() 方法现在接受二进制输入。

StringIO 和文本文件输入已被弃用。此外,如果使用非 ASCII 字符,字符串输入将提前失败。此前它会在后续步骤处理电子邮件时失败。

还支持二进制输出。get_file() 方法现在以二进制模式返回文件(以前错误地将文件设置为文本模式)。还有一个新的 get_bytes() 方法,它返回与给定 key 对应的 bytes 消息表示。

使用旧API的 get_string() 方法仍然可以获得非二进制输出,但这种方法并不非常有用。相反,最好是从 Message 对象中提取消息,或者从二进制输入中加载它们。

(由R. David Murray贡献,Steffen Daode Nurpmeso参与努力,Victor Stinner在 bpo-9124 中提供初始补丁。)

turtledemo

turtle 模块的演示代码已从 Demo 目录移至主库。它包含十多个带有生动显示的示例脚本。由于现在位于 sys.path 中,可以直接从命令行运行:

$ python -m turtledemo

(由Alexander Belopolsky从Demo目录移至主库,见 bpo-10199。)

多线程

  • 用于序列化并发运行的Python线程的机制(通常称为 GIL 或全局解释器锁)已被重写。此次改进的目标包括:实现更可预测的线程切换间隔,以及减少因锁竞争和后续系统调用数量导致的开销。原有的"检查间隔"(用于控制线程切换的机制)概念已被废弃,取而代之的是以秒为单位的绝对时长。该参数可通过 sys.setswitchinterval() 进行调节,当前默认值为5毫秒。

    有关该实现的更多详细信息可以从 python-dev 邮件列表消息 中阅读(但需注意:该消息中提到的"优先级请求"机制并未被最终采纳)。

    (由 Antoine Pitrou 贡献。)

  • 常规和递归锁现在在其 acquire() 方法中接受一个可选的 timeout 参数。(由 Antoine Pitrou 在 bpo-7316 中贡献。)

  • 同样,threading.Semaphore.acquire() 也增加了一个 timeout 参数。(由 Torsten Landschoff 在 bpo-850728 中贡献。)

  • 在使用 Pthreads 的平台上,常规锁和递归锁的获取操作现在可以被信号中断。这意味着Python 程序在获取锁时发生死锁,可以通过反复向该进程发送 SIGINT 信号(在大多数 shell 中按下 Ctrl+C)来成功终止程序。(由 Reid Kleckner 在 bpo-8844 中贡献。)

性能优化

添加了一些小的性能提升:

  • Python 的窥孔优化器(peephole optimizer)现在能够识别诸如 x in {1, 2, 3} 这样的模式,将其判定为对常量集合的成员资格测试。优化器会将 set (集合) 重新转换为 frozenset (不可变集合) ,并存储这个预先构建的常量。

    既然速度开销已不复存在,现在可以放心地开始使用集合表示法来编写成员资格测试了。这种写法不仅语义清晰,而且执行高效:

    extension = name.rpartition('.')[2]
    if extension in {'xml', 'html', 'xhtml', 'css'}:
        handle(name)
    

    (补丁和附加测试由 Dave Malcolm 在 bpo-6690 中贡献)。

  • 使用 pickle 模块序列化和反序列化数据的速度现在快了几倍。

    (由 Alexandre Vassalotti, Antoine Pitrou 和 Unladen Swallow 团队在 bpo-9410bpo-3873 中贡献。)

  • list.sort()sorted() 中使用的 Timsort 算法 现在在有 键函数 的情况下运行更快且使用更少内存。之前,列表的每个元素都会被一个临时对象包裹,该对象记住与每个元素关联的键值。现在,键和值的两个数组并行排序。这节省了排序包装器消耗的内存,并节省了委托比较所花费的时间。

    (由 Daniel Stutzbach 在 bpo-9915 中提交补丁。)

  • JSON 解码性能得到提升,且当同一字符串被用作多个键值时内存消耗降低。此外,当 sort_keys 参数为 True 时,JSON 编码现会启用 C 语言加速模块。

    (由Antoine Pitrou 在 bpo-7451 中贡献,由 Raymond Hettinger 和 Antoine Pitrou 在 bpo-10314 中贡献。)

  • 递归锁(使用 threading.RLock() API 创建)现在受益于 C 实现,这使得它们与常规锁一样快,并且比之前的纯 Python 实现快 10 到 15 倍。

    (由 Antoine Pitrou 在 bpo-3001 中贡献。)

  • stringlib 中的快速搜索算法现在被 bytesbytearraystr 对象的 split()rsplit()splitlines()replace() 方法使用。同样,该算法也被 rfind()rindex()rsplit()rpartition() 方法使用。

    (由 Florent Xicluna 在 bpo-7622bpo-7462 中提交补丁。)

  • 整型转字符串操作现在改为每次处理两个"数字位",从而减少了除法和取模运算的次数。

    (由 Gawain Bolton、Mark Dickinson 和 Victor Stinner 在 bpo-6713 中提交。)

还进行了其他一些小的优化。当一个操作数远大于另一个时,集合差分现在运行得更快(由 Andress Bennetts 在 bpo-8685 中提交补丁)。array.repeat() 方法有了更快的实现(由 Alexander Belopolsky 在 bpo-1569291 中提交)。BaseHTTPRequestHandler 的缓冲更加高效(由 Andrew Schaaf 在 bpo-3709 中提交)。operator.attrgetter() 函数的速度得到了提升(由 Christos Georgiou 在 bpo-10160 中提交)。ConfigParser 加载多行参数的速度也有所提高(由 Łukasz Langa 在 bpo-7113 中提交)。

Unicode

Python 已更新到 Unicode 6.0.0。此次更新新增了超过 2000 个字符,包括对手机重要的 emoji 符号。

此外,更新后的标准修改了两个卡纳达文字符(U+0CF1、U+0CF2)和一个新傣仂数字字符(U+19DA)的字符属性:前者现在可用于标识符,而后者则不再符合标识符使用要求。更多详细信息请参阅 Unicode字符数据库变更说明

编解码器

添加了对 cp720 阿拉伯 DOS 编码的支持(bpo-1616979)。

MBCS 编码不再忽略错误处理程序参数。在默认的严格模式下,当遇到无法解码的字节序列时,它会引发 UnicodeDecodeError 异常;当遇到无法编码的字符时,会引发 UnicodeEncodeError 异常。

多字节字符集编解码器(MBCS codec)在解码时支持 'strict' (严格模式) 和 'ignore' (忽略模式) 两种错误处理器,在编码时则支持 'strict' (严格模式) 和 'replace' (替换模式) 。

若需模拟 Python 3.1 版本的 MBCS 编码行为,应在解码时选用 'ignore' (忽略模式) 错误处理器,而在编码时选用 'replace' (替换模式) 错误处理器。

在 Mac OS X 上,Python 使用 'utf-8' 而不是区域设置编码来解码命令行参数。

默认情况下,tarfile 在 Windows 上使用 'utf-8' 编码格式 (而不是 'mbcs') 并在所有操作系统上使用 'surrogateescape' 错误处理器。

文档

文档继续得到改进。

  • 在长篇幅的章节(如 内置函数)顶部添加了一个快速链接表。以 itertools 为例,这些链接旁边附有速查表风格的摘要表格,无需通读完整文档即可快速掌握要点、唤醒记忆。

  • 在某些情况下,纯 Python 源代码可以作为文档的有益补充,因此现在许多模块都提供了指向最新版本源代码的快速链接。例如,functools 模块文档顶部有一个快速链接,标记为:

    源代码 Lib/functools.py.

    (由 Raymond Hettinger 贡献,参见 rationale。)

  • 当前文档现包含更多实用示例与技巧指南。特别地,re 模块新增了内容详尽的正则表达式示例专章 正则表达式例子;同样地,itertools 模块也持续更新,不断增添新的迭代器工具配方 itertools 配方

  • datetime 模块现在有一个纯 Python 的辅助实现。功能没有变化。这只是为了提供一个更易读的替代实现。

    (由 Alexander Belopolsky 在 bpo-9528 中贡献。)

  • 未维护的 Demo 目录已被删除。一些演示被整合到文档中,一些被移动到 Tools/demo 目录,其他的则被完全删除。

    (由 Georg Brandl 在 bpo-7962 中贡献)

IDLE

  • 格式菜单现在有一个选项,用于通过删除尾部空格来清理源文件。

    (由 Raymond Hettinger 在 bpo-5150 中贡献。)

  • Mac OS X 上的 IDLE 现在既支持 Carbon AquaTk 也支持 Cocoa AquaTk。

    (由 Kevin Walzer, Ned Deily 和 Ronald Oussoren 在 bpo-6075 中贡献。)

代码库

除了现有的 Subversion 代码仓库位于 https://svn.python.org 外,现在还有一个 Mercurial 仓库位于 https://hg.python.org/

在 3.2 版本发布后,计划将 Mercurial 作为主要仓库。这种分布式版本控制系统应使社区成员更容易创建和共享外部更改集。详见 PEP 385

要学习使用新的版本控制系统,请参阅 Quick StartGuide to Mercurial Workflows

构建和 C API 的改变

针对 Python 构建过程和 C API 的改变包括:

  • 现在 idle, pydoc2to3 脚本的安装将在 make altinstall 中附带特定版本的后缀 (bpo-10679)。

  • 访问 Unicode 数据库的 C 函数现在接受并返回完整 Unicode 范围内的字符,即使在窄 Unicode 构建中也是如此(Py_UNICODE_TOLOWER、Py_UNICODE_ISDECIMAL 等)。在 Python 中可见的区别是 unicodedata.numeric() 现在为大代码点返回正确的值,而 repr() 可能有更多字符被视为可打印。

    (由 Bupjoe Lee 报告,Amaury Forgeot D'Arc 修复;参见 bpo-5127 。)

  • 计算跳转(computed gotos)现在在支持的编译器(支持情况由配置脚本检测)上默认启用。仍可通过指定 --without-computed-gotos 选择性禁用。

    (由 Antoine Pitrou 在 bpo-9203 中贡献。)

  • 选项 --with-wctype-functions 已被移除。内置的 Unicode 数据库现在用于所有函数。

    (由 Amaury Forgeot d'Arc 在 bpo-9210 中贡献。)

  • 哈希值现在采用一种新类型 Py_hash_t 的值,该类型被定义为与指针具有相同的大小。此前,哈希值的类型为 long,在某些 64 位操作系统上,long 仍然只有 32 位长。得益于这一改进,setdict 现在可以在使用 64 位指针的构建版本中容纳超过 2**32 个条目(此前,虽然它们能够增长到该规模,但其性能会急剧下降)。

    (由 Raymond Hettinger 建议,Benjamin Peterson 实现;参见 bpo-9778。)

  • 一个新的宏 Py_VA_COPY 复制可变参数列表的状态。它等同于 C99 的 va_copy,但在所有 Python 平台上都可用(bpo-2443)。

  • 一个新的 C API 函数 PySys_SetArgvEx() 允许嵌入式解释器设置 sys.argv 而不修改 sys.path (bpo-5753)。

  • PyEval_CallObject() 现在仅以宏形式提供。为了向后兼容而保留的函数声明现已移除——该宏于1997年引入 (bpo-8276)。

  • 新增函数 PyLong_AsLongLongAndOverflow(),它与 PyLong_AsLongAndOverflow() 类似。它们都用于将 Python int 转换为本地固定宽度类型,并提供检测转换不匹配的情况 (bpo-7767)。

  • PyUnicode_CompareWithASCIIString() 函数现在在 Python 字符串以 NUL 结尾时返回 不相等

  • 新增函数 PyErr_NewExceptionWithDoc(),类似于 PyErr_NewException(),但允许指定文档字符串。这使得 C 语言实现的异常能够像纯 Python 异常一样具备自文档化能力 (bpo-7033)。

  • 当使用 --with-valgrind 选项编译时,pymalloc 分配器在 Valgrind 下运行时会自动禁用。这提高了在 Valgrind 下运行时的内存泄漏检测能力,同时在其他时候利用 pymalloc (bpo-2422)。

  • PyArg_Parse 函数中移除了 O? 格式。该格式不再使用,且从未被文档化 (bpo-8837)。

C-API 还有一些其他小改动。详见 Misc/NEWS 文件以获取完整列表。

此外,Mac OS X 构建版本进行了多项更新,详细信息请参阅 Mac/BuildScript/README.txt。对于运行32/64位构建版本的用户,已知在Mac OS X 10.6上默认的Tcl/Tk存在问题。因此,我们建议安装更新的替代版本,例如 ActiveState Tcl/Tk 8.5.9。更多详情请访问 https://www.python.org/download/mac/tcltk/

移植到 Python 3.2

本节列出了先前描述的改变以及可能需要修改你的代码的其他问题修正:

  • configparser 模块进行了一系列清理工作。主要变化是将旧的 ConfigParser 类替换为长期以来首选的替代类 SafeConfigParser。此外,还有一些较小的兼容性问题:

    • 插值语法现在在 get()set() 操作中进行验证。在默认的插值方案中,只有两个带有百分号的标记是有效的:%(name)s%%,后者是转义后的百分号。

    • set()add_section() 方法现在验证值是否为实际的字符串。以前,可能会无意引入不支持的类型。

    • 来自单个源的重复节或选项现在会引发 DuplicateSectionErrorDuplicateOptionError。此前,重复项会默默地覆盖先前的条目。

    • 内联注释现在默认禁用,因此现在可以安全地在值中使用 ; 字符。

    • 注释现在可以缩进。因此,为了在多行值的行首出现 ;#,必须进行插值。这样可以防止值中的注释前缀字符被误认为是注释。

    • "" 现在是一个有效值,不再自动转换为空字符串。对于空字符串,请在行中使用 "option ="

  • nntplib 模块进行了大量重写,这意味着其 API 通常与 3.1 版本的 API 不兼容。

  • bytearray 对象不能再用作文件名;相反,它们应该转换为 bytes

  • array.tostring()array.fromstring() 已更名为 array.tobytes()array.frombytes() 以提高清晰度。旧名称已被弃用。(参见 bpo-8990。)

  • PyArg_Parse*() 函数:

    • "t#" 格式已被移除:改用 "s#" 或 "s*"

    • "w" 和 "w#" 格式已被移除:改用 "w*"

  • 在 3.1 版本中已被弃用的 PyCObject 类型已被移除。为了在 Python 对象中包装不透明的 C 指针,应使用 PyCapsule API;新类型具有明确定义的接口,用于传递类型安全信息,并且调用析构函数的签名更简单。

  • sys.setfilesystemencoding() 函数已被移除,因为其设计存在缺陷。

  • random.seed() 函数和方法现在使用 sha512 哈希函数对字符串种子进行加盐。为了访问以前版本的 seed 以重现 Python 3.1 序列,将 version 参数设置为 1,即 random.seed(s, version=1)

  • 此前已弃用的 string.maketrans() 函数已被移除,取而代之的是静态方法 bytes.maketrans()bytearray.maketrans()。这一变更解决了关于 string 模块支持哪些数据类型的混淆问题。现在,strbytesbytearray 类型各自拥有独立的 maketranstranslate 方法,并会生成与相应类型匹配的中间转换表。

    (由Georg Brandl在 bpo-5675 中贡献)

  • 此前已弃用的 contextlib.nested() 函数已被移除,改为使用普通的 with 语句,该语句可以接受多个上下文管理器。后一种技术在速度上更快(因为它是内置的),并且在其中一个上下文管理器引发异常时,能更好地完成多个上下文管理器的终结处理:

    with open('mylog.txt') as infile, open('a.out', 'w') as outfile:
        for line in infile:
            if '<critical>' in line:
                outfile.write(line)
    

    (由 Georg Brandl 和 Mattias Brändström 贡献; appspot issue 53094。)

  • struct.pack() 现在只允许使用字节类型(bytes)作为 s 字符串打包代码的参数。此前,该函数会接受文本类型(text)参数,并隐式地使用 UTF-8 编码将其转换为字节类型。这种做法存在两个问题:一是它对正确的编码方式做了预设假设;二是在将变长编码写入结构的固定长度字段时可能会导致失败。

    类似于 struct.pack('<6sHHBBB', 'GIF87a', x, y) 的代码应重写为使用字节串而不是文本,即 struct.pack('<6sHHBBB', b'GIF87a', x, y)

    (由 David Beazley 发现,并由 Victor Stinner 修复;参见 bpo-10783。)

  • xml.etree.ElementTree 类在解析失败时现在会抛出 xml.etree.ElementTree.ParseError。之前,它会抛出 xml.parsers.expat.ExpatError

  • 新的、更长的浮点数 str() 输出格式可能会导致依赖旧输出格式的 doctest 测试用例失效。

  • subprocess.Popen 中,close_fds 的默认值在 Unix 上现在为 True;在 Windows 上,如果三个标准流均设置为 None,则为 True,否则为 False。之前,close_fds 默认总是 False,这会导致打开的文件描述符泄露到子进程中,产生难以解决的错误或竞争条件。

  • 对旧式 HTTP 0.9 的支持已从 urllib.requesthttp.client 中移除。 此项支持仍然存在于服务器端(在 http.server 中)。

    (由 Antoine Pitrou 在 bpo-10711 中贡献。)

  • 超时模式下的 SSL 套接字现在如发生超时则会引发 socket.timeout,而不是一般性的 SSLError

    (由 Antoine Pitrou 在 bpo-10272 中贡献。)

  • 有误导性的 PyEval_AcquireLock()PyEval_ReleaseLock() 已正式被弃用。 应当改用可感知线程状态的 API (如 PyEval_SaveThread()PyEval_RestoreThread())。

  • 由于存在安全风险,asyncore.handle_accept() 已被弃用,新增了一个函数 asyncore.handle_accepted() 用来替代它。

    (由 Giampaolo Rodola 在 bpo-6706 中贡献。)

  • 由于新的 GIL 实现的限制,PyEval_InitThreads() 不再可以在 Py_Initialize() 之前被调用。