这是关于 Python 3.x 版本中首次出现的功能系列文章的第四篇。Python 3.3 于 2012 年首次发布,即使它已经发布很长时间了,但它引入的许多功能仍然未被充分利用,而且非常酷。这里有其中三个功能。
yield from
yield
关键字使 Python 更加强大。不出所料,每个人都开始使用它来创建一个完整的迭代器生态系统。itertools 模块和 more-itertools PyPI 包只是两个例子。
有时,一个新的生成器会想要使用一个现有的生成器。作为一个简单的(如果有些牵强的)例子,想象一下你想枚举所有自然数对。
一种方法是按照 pair 的和,pair 的第一个元素
的顺序生成所有 pair。使用 yield from
实现这一点是很自然的。
yield from <x>
关键字是以下代码的简写:
for item in x:
yield item
import itertools
def pairs():
for n in itertools.count():
yield from ((i, n-i) for i in range(n+1))
list(itertools.islice(pairs(), 6))
[(0, 0), (0, 1), (1, 0), (0, 2), (1, 1), (2, 0)]
隐式命名空间包
想象一家名为 Parasol 的虚构公司,该公司生产很多东西。其大部分内部软件都是用 Python 编写的。虽然 Parasol 已经开源了部分代码,但其中一些代码过于专有或专业,不适合开源。
该公司使用内部 DevPI 服务器来管理内部软件包。Parasol 的每位 Python 程序员都在 PyPI 上找到一个未使用的名称是没有意义的,因此所有内部软件包都称为 parasol.<业务部门>.<项目>
。为了遵守最佳实践,开发人员希望包名称反映该命名系统。
这很重要!如果软件包 parasol.accounting.numeric_tricks
安装了一个名为 numeric_tricks
的顶级模块,这意味着任何依赖于此软件包的人都将无法使用名为 numeric_tricks
的 PyPI 软件包,无论它多么巧妙。
然而,这给开发人员留下了一个难题:哪个软件包拥有 parasol/__init__.py
文件?从 Python 3.3 开始,最好的解决方案是将 parasol
,以及可能的 parasol.accounting
,设为 命名空间包,它们没有 __init__.py
文件。
抑制异常上下文
有时,在从异常中恢复的过程中发生异常是一个问题,并且拥有上下文来跟踪它是有用的。但是,有时情况并非如此:异常已被处理,并且新情况是不同的错误条件。
例如,想象一下,在字典中查找键失败后,如果无法分析该键,则希望使用 ValueError()
失败
import time
def expensive_analysis(data):
time.sleep(10)
if data[0:1] == ">":
return data[1:]
return None
此函数需要很长时间,因此当您使用它时,您希望缓存结果
cache = {}
def last_letter_analyzed(data):
try:
analyzed = cache[data]
except KeyError:
analyzed = expensive_analysis(data)
if analyzed is None:
raise ValueError("invalid data", data)
cached[data] = analyzed
return analyzed[-1]
不幸的是,当发生缓存未命中时,回溯看起来很糟糕
last_letter_analyzed("stuff")
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-16-a525ae35267b> in last_letter_analyzed(data)
4 try:
----> 5 analyzed = cache[data]
6 except KeyError:
KeyError: 'stuff'
在处理上述异常期间,又发生了一个异常
ValueError Traceback (most recent call last)
<ipython-input-17-40dab921f9a9> in <module>
----> 1 last_letter_analyzed("stuff")
<ipython-input-16-a525ae35267b> in last_letter_analyzed(data)
7 analyzed = expensive_analysis(data)
8 if analyzed is None:
----> 9 raise ValueError("invalid data", data)
10 cached[data] = analyzed
11 return analyzed[-1]
ValueError: ('invalid data', 'stuff')
如果您使用 raise ... from None
,您可以获得更易读的回溯
def last_letter_analyzed(data):
try:
analyzed = cache[data]
except KeyError:
analyzed = expensive_analysis(data)
if analyzed is None:
raise ValueError("invalid data", data) from None
cached[data] = analyzed
return analyzed[-1]
last_letter_analyzed("stuff")
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-21-40dab921f9a9> in <module>
----> 1 last_letter_analyzed("stuff")
<ipython-input-20-5691e33edfbc> in last_letter_analyzed(data)
5 analyzed = expensive_analysis(data)
6 if analyzed is None:
----> 7 raise ValueError("invalid data", data) from None
8 cached[data] = analyzed
9 return analyzed[-1]
ValueError: ('invalid data', 'stuff')
欢迎来到 2012 年
虽然 Python 3.3 几乎在十年前发布,但它的许多功能仍然很酷——而且未被充分利用。如果您还没有这样做,请将它们添加到您的工具包中。
评论已关闭。