Python 3.1 的新特性 — Python 文档

来自菜鸟教程
Python/docs/3.9/whatsnew/3.1
跳转至:导航、​搜索

Python 3.1 中的新功能

作者
雷蒙德·赫廷格

本文解释了 Python 3.1 中与 3.0 相比的新特性。

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 编写的实现。


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 - 千位分隔符的格式说明符
PEP 由 Raymond Hettinger 编写,由 Eric Smith 和 Mark Dickinson 实施。


其他语言更改

对核心 Python 语言进行的一些较小更改是:

  • 包含 __main__.py 文件的目录和 zip 档案现在可以通过将它们的名称传递给解释器来直接执行。 目录/zip 文件会自动作为 sys.path 中的第一个条目插入。 (Andy Chu 的建议和初始补丁;Phillip J. 修订的补丁。 伊比和尼克科格兰; :问题:`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 提供;:问题:`3439`。)

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

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

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

    (由 Eric Smith 提供;:issue:`5237`。)

  • string.maketrans() 函数已弃用,取而代之的是新的静态方法,bytes.maketrans()bytearray.maketrans()。 此更改解决了 string 模块支持哪些类型的混淆。 现在,strbytesbytearray 都有自己的 maketranstranslate 方法和中间转换表适当的类型。

    (由 Georg Brandl 提供;:问题:`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 提供;:issue:`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 提供;:issue:`1580`


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

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

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

    (由 Raymond Hettinger 提供;:问题:`1696199`。)

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

    (由 Guilherme Polo 提供;:issue:`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.Fraction十进制.十进制

    >>> [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)]

    (雷蒙德·赫廷格供稿。)

  • 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 提供;:issue:`1818`。)

  • re.sub()re.subn()re.split() 函数现在接受标志参数。

    (格雷戈里·史密斯供稿。)

  • logging 模块现在为不使用日志记录但调用库代码的应用程序实现了一个简单的 logging.NullHandler 类。 设置空处理程序将抑制虚假警告,例如“找不到记录器 foo 的处理程序”:

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

    (由 Vinay Sajip 提供;:issue:`4384`)。

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

    (由 Andi Vajda 提供;:issue:`4195`。)

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

    (由 Alexander Belopolsky 提供;:issue:`4201`。)

  • functools.partial 对象现在可以被腌制。

(由 Antoine Pitrou 和 Jesse Noller 推荐。 由 Jack Diederich 实施; :问题:`5228`。)


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

    (由 David Laban 提供;:issue:`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()

    (本杰明·彼得森和安托万·皮特鲁供稿。)

  • 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 提供;:issue:`4285`。)

  • nntplibimaplib 模块现在支持 IPv6。

    (由 Derek Morr 提供;:issue:`1655`:issue:`1664`。)

  • pickle 模块在与协议 2 或更低版本一起使用时已经过调整,以实现与 Python 2.x 更好的互操作性。 标准库的重组改变了许多对象的正式引用。 例如,Python 2 中的 __builtin__.set 在 Python 3 中称为 builtins.set。 这种变化混淆了在不同版本的 Python 之间共享数据的努力。 但现在选择协议2或更低时,泡泡器将自动使用旧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 泡菜将无法用 Python 3.0 读取。 在 Python 3.x 实现之间迁移数据时应使用最新的 pickle 协议协议 3,因为它不会尝试与 Python 2.x 保持兼容。

    (由 Alexandre Vassalotti 和 Antoine Pitrou 提供,:issue:`6137`。)

  • 添加了一个新模块 importlib。 它提供了 import 语句及其对应的 __import__() 函数的完整、可移植、纯 Python 参考实现。 它代表了在记录和定义进口期间发生的行动方面向前迈出的重要一步。

    (由布雷特·坎农提供。)


优化

添加了主要的性能增强功能:

  • 新的 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 提供,:issue:`4688`。)

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

    (由 Antoine Pitrou 和其他一些参与者提供,:issue:`4753`)。

  • UTF-8、UTF-16 和 LATIN-1 的解码速度提高了两到四倍。

    (由 Antoine Pitrou 和 Amaury Forgeot d'Arc 提供,:issue:`4868`。)

  • json 模块现在有一个 C 扩展来显着提高其性能。 此外,API 被修改为 json 仅适用于 str,而不适用于 bytes。 这一变化使模块与根据 Unicode 定义的 JSON 规范 紧密匹配。

    (由 Bob Ippolito 提供,并由 Antoine Pitrou 和 Benjamin Peterson 转换为 Py3.1;:issue:`4136`。)

  • Unpickling 现在会保留腌制对象的属性名称。 这可以节省内存并允许泡菜更小。

    (由 Jake McGuire 和 Antoine Pitrou 提供;:issue:`5084`。)


空闲

  • IDLE 的格式菜单现在提供了从源文件中去除尾随空格的选项。

    (罗杰 D 供稿) 塞尔维; :问题:`5150`。)


构建和 C API 更改

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

  • 整数现在内部存储在基数 2**15 或基数 2**30 中,基数在构建时确定。 以前,它们总是存储在基数 2**15 中。 使用 base 2**30 在 64 位机器上有显着的性能改进,但在 32 位机器上的基准测试结果好坏参半。 因此,默认是在64位机器上使用base 2**30,在32位机器上使用base 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 提供;:issue:`4258`。)

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

    (由 Mark Dickinson 和 Lisandro Dalcrin 提供;:issue:`5175`。)

  • 已弃用 PyNumber_Int()。 使用 PyNumber_Long() 代替。

    (由 Mark Dickinson 提供;:issue:`4910`。)

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

    (由 Mark Dickinson 提供;:issue:`5914`。)

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

    (由拉里·黑斯廷斯提供;:issue:`5630`。)


移植到 Python 3.1

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

  • 新的浮点字符串表示可以破坏现有的 doctests。 例如:

    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
    **********************************************************************
  • 协议 2 或更低版本的 pickle 模块中的自动名称重新映射会使 Python 3.1 pickle 在 Python 3.0 中不可读。 一种解决方案是使用协议 3。 另一种解决方案是将 fix_imports 选项设置为 False。 有关更多详细信息,请参阅上面的讨论。