Python 3.1 新特性

作者:

Raymond Hettinger

本文介绍了 Python 3.1 相对于 3.0 的新特性。Python 3.1 于 2009 年 6 月 27 日发布。

PEP 372:有序字典

常规 Python 字典以任意顺序迭代键/值对。多年来,许多作者编写了替代实现,可以记住最初插入键的顺序。基于这些实现的经验,引入了一个新的 collections.OrderedDict 类。

OrderedDict API 与常规字典基本相同,但会根据键首次插入的时间以保证的顺序迭代键和值。如果新条目覆盖现有条目,则原始插入位置保持不变。删除条目并重新插入会将其移动到末尾。

标准库现在在多个模块中支持使用有序字典。configparser 模块默认使用它们。这使得配置文件可以按照原始顺序读取、修改和写回。collections.namedtuple()_asdict() 方法现在返回一个有序字典,其中的值与基础元组索引的顺序相同。json 模块正在使用 object_pairs_hook 进行构建,以允许解码器构建 OrderedDicts。还增加了对 PyYAML 等第三方工具的支持。

另请参阅

PEP 372 - 有序字典

由 Armin Ronacher 和 Raymond Hettinger 编写的 PEP。由 Raymond Hettinger 实现。

由于有序字典会记住其插入顺序,因此可以与排序结合使用来创建排序字典

>>> # regular unsorted dictionary
>>> d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}

>>> # dictionary sorted by key
>>> OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])

>>> # dictionary sorted by value
>>> OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

>>> # dictionary sorted by length of the key string
>>> OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
OrderedDict([('pear', 1), ('apple', 4), ('orange', 2), ('banana', 3)])

新的排序字典在删除条目时会保持其排序顺序。但是,当添加新键时,键会追加到末尾,并且不会保持排序。

PEP 378:千位分隔符格式说明符

内置的 format() 函数和 str.format() 方法使用一种微型语言,现在包含一种简单、不区分区域设置的方式来格式化带有千位分隔符的数字。这提供了一种使程序输出人性化的方法,提高了其专业外观和可读性

>>> format(1234567, ',d')
'1,234,567'
>>> format(1234567.89, ',.2f')
'1,234,567.89'
>>> format(12345.6 + 8901234.12j, ',f')
'12,345.600000+8,901,234.120000j'
>>> format(Decimal('1234567.89'), ',f')
'1,234,567.89'

支持的类型包括 intfloatcomplexdecimal.Decimal

正在讨论如何指定诸如点、空格、撇号或下划线之类的替代分隔符。区分区域设置的应用程序应使用现有的 n 格式说明符,该说明符已经对千位分隔符提供了一些支持。

另请参阅

PEP 378 - 千位分隔符格式说明符

由 Raymond Hettinger 编写的 PEP,由 Eric Smith 和 Mark Dickinson 实现。

其他语言变更

对核心 Python 语言所做的一些较小的更改是

  • 现在,可以通过将包含 __main__.py 文件的目录和 zip 归档文件直接传递给解释器来执行它们。目录/zip 文件会自动作为 sys.path 中的第一个条目插入。(Andy Chu 的建议和初始补丁;Phillip J. Eby 和 Nick Coghlan 的修订补丁;bpo-1739468。)

  • int() 类型获得了一个 bit_length 方法,该方法返回以二进制表示其参数所需的位数

    >>> n = 37
    >>> bin(37)
    '0b100101'
    >>> n.bit_length()
    6
    >>> n = 2**123-1
    >>> n.bit_length()
    123
    >>> (n+1).bit_length()
    124
    

    (由 Fredrik Johansson、Victor Stinner、Raymond Hettinger 和 Mark Dickinson 贡献;bpo-3439。)

  • 现在可以自动编号 format() 字符串中的字段

    >>> 'Sir {} of {}'.format('Gallahad', 'Camelot')
    'Sir Gallahad of Camelot'
    

    以前,该字符串将需要编号的字段,例如:'Sir {0} of {1}'

    (由 Eric Smith 贡献;bpo-5237。)

  • string.maketrans() 函数已弃用,并被新的静态方法 bytes.maketrans()bytearray.maketrans() 替换。此更改解决了围绕 string 模块支持哪些类型的困惑。现在,strbytesbytearray 各自都有自己的 maketranstranslate 方法,并带有适当类型的中间转换表。

    (由 Georg Brandl 贡献;bpo-5675。)

  • with 语句的语法现在允许单个语句中的多个上下文管理器

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

    使用新语法,不再需要 contextlib.nested() 函数,并且现在已弃用。

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

  • 如果 x 是整数,则 round(x, n) 现在返回整数。以前它返回浮点数

    >>> round(1123, -2)
    1100
    

    (由 Mark Dickinson 贡献;bpo-4707。)

  • Python 现在使用 David Gay 的算法来查找不会更改其值的最短浮点表示形式。这应该有助于缓解围绕二进制浮点数的一些困惑。

    对于像 1.1 这样的数字,其意义很容易看到,因为二进制浮点数中没有完全等效的值。由于没有完全等效的值,因此像 float('1.1') 这样的表达式的求值结果为最接近的可表示值,该值在十六进制中为 0x1.199999999999ap+0 或在十进制中为 1.100000000000000088817841970012523233890533447265625。该最接近的值过去和现在都用于后续的浮点计算。

    新的是数字的显示方式。以前,Python 使用一种简单的方法。repr(1.1) 的值计算为 format(1.1, '.17g'),其求值结果为 '1.1000000000000001'。使用 17 位数字的优点在于,它依靠 IEEE-754 保证来确保 eval(repr(1.1)) 将精确地往返到其原始值。缺点是许多人发现输出令人困惑(将二进制浮点表示的内在局限性误认为是 Python 本身的问题)。

    repr(1.1) 的新算法更智能,并返回 '1.1'。实际上,它会搜索所有等效的字符串表示形式(使用相同的基础浮点值存储的表示形式),并返回最短的表示形式。

    新算法倾向于在可能的情况下发出更简洁的表示形式,但它不会更改基础值。因此,即使表示形式可能暗示相反,1.1 + 2.2 != 3.3 的情况仍然存在。

    新算法取决于基础浮点实现中的某些功能。如果找不到所需的功能,将继续使用旧算法。此外,文本 pickle 协议通过使用旧算法来保证跨平台的可移植性。

    (由 Eric Smith 和 Mark Dickinson 贡献;bpo-1580

新增、改进和已弃用的模块

  • 添加了 collections.Counter 类,以支持方便地统计序列或可迭代对象中唯一项的数量。

    >>> Counter(['red', 'blue', 'red', 'green', 'blue', 'blue'])
    Counter({'blue': 3, 'red': 2, 'green': 1})
    

    (由 Raymond Hettinger 贡献;bpo-1696199.)

  • 添加了一个新的模块 tkinter.ttk,用于访问 Tk 主题化窗口部件集。ttk 的基本思想是将实现窗口部件行为的代码与实现其外观的代码尽可能地分离。

    (由 Guilherme Polo 贡献;bpo-2983.)

  • gzip.GzipFilebz2.BZ2File 类现在支持上下文管理协议。

    >>> # Automatically close file after writing
    >>> with gzip.GzipFile(filename, "wb") as f:
    ...     f.write(b"xxx")
    

    (由 Antoine Pitrou 贡献.)

  • decimal 模块现在支持从二进制 float 创建十进制对象的方法。转换是精确的,但有时可能会令人惊讶。

    >>> Decimal.from_float(1.1)
    Decimal('1.100000000000000088817841970012523233890533447265625')
    

    长的十进制结果显示了为 *1.1* 存储的实际二进制小数。该小数有很多位,因为 *1.1* 不能以二进制精确表示。

    (由 Raymond Hettinger 和 Mark Dickinson 贡献.)

  • itertools 模块增加了两个新函数。itertools.combinations_with_replacement() 函数是生成包括排列和笛卡尔积在内的组合数学的四个函数之一。itertools.compress() 函数模仿了 APL 中的同名函数。此外,现有的 itertools.count() 函数现在有一个可选的 *step* 参数,并且可以接受任何类型的计数序列,包括 fractions.Fractiondecimal.Decimal

    >>> [p+q for p,q in combinations_with_replacement('LOVE', 2)]
    ['LL', 'LO', 'LV', 'LE', 'OO', 'OV', 'OE', 'VV', 'VE', 'EE']
    
    >>> list(compress(data=range(10), selectors=[0,0,1,1,0,1,0,1,0,0]))
    [2, 3, 5, 7]
    
    >>> c = count(start=Fraction(1,2), step=Fraction(1,6))
    >>> [next(c), next(c), next(c), next(c)]
    [Fraction(1, 2), Fraction(2, 3), Fraction(5, 6), Fraction(1, 1)]
    

    (由 Raymond Hettinger 贡献.)

  • collections.namedtuple() 现在支持关键字参数 *rename*,它可以让无效的字段名自动转换为 _0, _1 等形式的位置名称。当字段名是由外部来源(如 CSV 标头、SQL 字段列表或用户输入)创建时,这非常有用。

    >>> query = input()
    SELECT region, dept, count(*) FROM main GROUPBY region, dept
    
    >>> cursor.execute(query)
    >>> query_fields = [desc[0] for desc in cursor.description]
    >>> UserQuery = namedtuple('UserQuery', query_fields, rename=True)
    >>> pprint.pprint([UserQuery(*row) for row in cursor])
    [UserQuery(region='South', dept='Shipping', _2=185),
     UserQuery(region='North', dept='Accounting', _2=37),
     UserQuery(region='West', dept='Sales', _2=419)]
    

    (由 Raymond Hettinger 贡献;bpo-1818.)

  • re.sub()re.subn()re.split() 函数现在接受一个 flags 参数。

    (由 Gregory Smith 贡献.)

  • logging 模块现在实现了一个简单的 logging.NullHandler 类,用于不使用日志记录但调用了日志记录的库代码的应用程序。设置一个空处理器将抑制诸如 “No handlers could be found for logger foo” 之类的虚假警告。

    >>> h = logging.NullHandler()
    >>> logging.getLogger("foo").addHandler(h)
    

    (由 Vinay Sajip 贡献;bpo-4384).

  • runpy 模块支持 -m 命令行开关,现在通过查找并执行包名称提供时的 __main__ 子模块来支持包的执行。

    (由 Andi Vajda 贡献;bpo-4195.)

  • pdb 模块现在可以访问和显示通过 zipimport (或任何其他符合规范的 PEP 302 加载器)加载的源代码。

    (由 Alexander Belopolsky 贡献;bpo-4201.)

  • functools.partial 对象现在可以被 pickle 序列化。

(由 Antoine Pitrou 和 Jesse Noller 建议。由 Jack Diederich 实现;bpo-5228.)

  • 为符号添加 pydoc 帮助主题,以便 help('@') 在交互式环境中按预期工作。

    (由 David Laban 贡献;bpo-4739.)

  • unittest 模块现在支持跳过单独的测试或测试类。它还支持将测试标记为预期失败,即已知存在问题的测试,但它不应在 TestResult 中被计为失败。

    class TestGizmo(unittest.TestCase):
    
        @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
        def test_gizmo_on_windows(self):
            ...
    
        @unittest.expectedFailure
        def test_gimzo_without_required_library(self):
            ...
    

    此外,异常的测试已扩展到使用 with 语句与上下文管理器一起工作。

    def test_division_by_zero(self):
        with self.assertRaises(ZeroDivisionError):
            x / 0
    

    此外,还添加了一些新的断言方法,包括 assertSetEqual(), assertDictEqual(), assertDictContainsSubset(), assertListEqual(), assertTupleEqual(), assertSequenceEqual(), assertRaisesRegexp(), assertIsNone(), 和 assertIsNotNone()

    (由 Benjamin Peterson 和 Antoine Pitrou 贡献.)

  • io 模块为 seek() 方法添加了三个新的常量:SEEK_SETSEEK_CURSEEK_END

  • sys.version_info 元组现在是一个命名元组。

    >>> sys.version_info
    sys.version_info(major=3, minor=1, micro=0, releaselevel='alpha', serial=2)
    

    (由 Ross Light 贡献;bpo-4285.)

  • nntplibimaplib 模块现在支持 IPv6。

    (由 Derek Morr 贡献;bpo-1655bpo-1664.)

  • 当使用协议 2 或更低版本时,pickle 模块已进行了调整,以便更好地与 Python 2.x 互操作。标准库的重组更改了许多对象的正式引用。例如,Python 2 中的 __builtin__.set 在 Python 3 中称为 builtins.set。此更改混淆了在不同 Python 版本之间共享数据的努力。但是现在,当选择协议 2 或更低版本时,pickler 将自动使用旧的 Python 2 名称进行加载和转储。此重映射默认启用,但可以使用 *fix_imports* 选项禁用。

    >>> s = {1, 2, 3}
    >>> pickle.dumps(s, protocol=0)
    b'c__builtin__\nset\np0\n((lp1\nL1L\naL2L\naL3L\natp2\nRp3\n.'
    >>> pickle.dumps(s, protocol=0, fix_imports=False)
    b'cbuiltins\nset\np0\n((lp1\nL1L\naL2L\naL3L\natp2\nRp3\n.'
    

    此更改的一个不幸但不可避免的副作用是,Python 3.1 生成的协议 2 pickle 不能被 Python 3.0 读取。在 Python 3.x 实现之间迁移数据时,应使用最新的 pickle 协议(协议 3),因为它不会尝试与 Python 2.x 保持兼容。

    (由 Alexandre Vassalotti 和 Antoine Pitrou 贡献,bpo-6137.)

  • 添加了一个新的模块 importlib。它提供了一个完整、可移植、纯 Python 的 import 语句及其对应函数 __import__() 的参考实现。它代表了在记录和定义导入过程中发生的操作方面向前迈出的一大步。

    (由 Brett Cannon 贡献。)

优化

添加了主要的性能增强

  • 新的 I/O 库(如 PEP 3116 中定义的那样)主要用 Python 编写,并很快被证明是 Python 3.0 中的一个瓶颈。在 Python 3.1 中,I/O 库已完全用 C 重写,速度提高了 2 到 20 倍,具体取决于手头的任务。纯 Python 版本仍然可以通过 _pyio 模块进行实验。

    (由 Amaury Forgeot d'Arc 和 Antoine Pitrou 贡献。)

  • 添加了一种启发式方法,以便垃圾回收器不会跟踪仅包含不可跟踪对象的元组和字典。这可以减少集合的大小,从而减少长期运行的程序中垃圾回收的开销,具体取决于它们对数据类型的特定使用。

    (由 Antoine Pitrou 贡献,bpo-4688。)

  • 在支持它的编译器(特别是:gcc、SunPro、icc)上启用名为 --with-computed-gotos 的配置选项,字节码评估循环将使用新的调度机制进行编译,根据系统、编译器和基准测试,速度可提高高达 20%。

    (由 Antoine Pitrou 与其他多位参与者一起贡献,bpo-4753)。

  • UTF-8、UTF-16 和 LATIN-1 的解码速度现在快了 2 到 4 倍。

    (由 Antoine Pitrou 和 Amaury Forgeot d'Arc 贡献,bpo-4868。)

  • json 模块现在有一个 C 扩展,以大幅提高其性能。此外,API 进行了修改,使 json 仅与 str 而不是 bytes 一起使用。该更改使该模块与以 Unicode 定义的 JSON 规范紧密匹配。

    (由 Bob Ippolito 贡献,并由 Antoine Pitrou 和 Benjamin Peterson 转换为 Py3.1;bpo-4136。)

  • 现在,反序列化会实习已序列化对象的属性名称。这可以节省内存并允许序列化文件更小。

    (由 Jake McGuire 和 Antoine Pitrou 贡献;bpo-5084。)

IDLE

  • IDLE 的格式菜单现在提供一个选项来删除源文件中的尾随空格。

    (由 Roger D. Serwy 贡献;bpo-5150。)

构建和 C API 更改

对 Python 的构建过程和 C API 的更改包括

  • 整数现在在内部以 2**15 为底或以 2**30 为底存储,底数在构建时确定。以前,它们始终以 2**15 为底存储。在 64 位机器上使用以 2**30 为底可以显着提高性能,但在 32 位机器上的基准测试结果好坏参半。因此,默认是在 64 位机器上使用以 2**30 为底,在 32 位机器上使用以 2**15 为底;在 Unix 上,有一个新的配置选项 --enable-big-digits 可用于覆盖此默认值。

    除了性能改进之外,此更改对于最终用户应该是不可见的,但有一个例外:出于测试和调试目的,有一个新的 sys.int_info,它提供有关内部格式的信息,给出每位数字的位数和用于存储每个数字的 C 类型的大小(以字节为单位)。

    >>> import sys
    >>> sys.int_info
    sys.int_info(bits_per_digit=30, sizeof_digit=4)
    

    (由 Mark Dickinson 贡献;bpo-4258。)

  • PyLong_AsUnsignedLongLong() 函数现在通过引发 OverflowError 而不是 TypeError 来处理负的 pylong

    (由 Mark Dickinson 和 Lisandro Dalcrin 贡献;bpo-5175。)

  • 已弃用 PyNumber_Int()。请改用 PyNumber_Long()

    (由 Mark Dickinson 贡献;bpo-4910。)

  • 添加了一个新的 PyOS_string_to_double() 函数来替换已弃用的函数 PyOS_ascii_strtod()PyOS_ascii_atof()

    (由 Mark Dickinson 贡献;bpo-5914。)

  • 添加了 PyCapsule 作为 PyCObject API 的替代品。主要区别在于,新类型具有明确定义的接口,用于传递类型安全信息,以及更简单的调用析构函数的签名。旧类型具有有问题的 API,现在已弃用。

    (由 Larry Hastings 贡献;bpo-5630。)

移植到 Python 3.1

本节列出了前面描述的更改和其他可能需要更改代码的错误修复

  • 新的浮点字符串表示形式可能会破坏现有的 doctest。例如

    def e():
        '''Compute the base of natural logarithms.
    
        >>> e()
        2.7182818284590451
    
        '''
        return sum(1/math.factorial(x) for x in reversed(range(30)))
    
    doctest.testmod()
    
    **********************************************************************
    Failed example:
        e()
    Expected:
        2.7182818284590451
    Got:
        2.718281828459045
    **********************************************************************
    
  • pickle 模块中对于协议 2 或更低版本的自动名称重新映射可能会使 Python 3.1 序列化文件在 Python 3.0 中不可读。一种解决方案是使用协议 3。另一种解决方案是将 fix_imports 选项设置为 False。有关更多详细信息,请参阅上面的讨论。