---
author: Moshe Zadka
category: 软件开发
comments_data: []
count:
  commentnum: 0
  favtimes: 0
  likes: 0
  sharetimes: 0
  viewnum: 2001
date: '2021-05-29 22:14:07'
editorchoice: false
excerpt: 探索异常处理和其他未被充分利用但仍然有用的 Python 特性。
fromurl: https://opensource.com/article/21/5/python-33
id: 13439
islctt: true
largepic: /images/p/202105/29/221357mxpj2kitltdez6zj.jpg
permalink: /article-13439-1.html
pic: /images/p/202105/29/221357mxpj2kitltdez6zj.jpg.thumb.jpg
related:
- displayorder: 0
  raid: 13435
- displayorder: 0
  raid: 13443
reviewer: wxy
selector: lujun9972
summary: 探索异常处理和其他未被充分利用但仍然有用的 Python 特性。
tags:
- Python
thumb: false
title: Python 3.3 为改进代码中的异常处理所做的工作
titlepic: true
translator: wxy
updated: '2021-05-29 22:14:07'
---


> 
> 探索异常处理和其他未被充分利用但仍然有用的 Python 特性。
> 
> 
> 


![](/images/p/202105/29/221357mxpj2kitltdez6zj.jpg "Coding on a computer")


这是 Python 3.x 首发特性系列文章的第四篇。Python 3.3 于 2012 年首次发布，尽管它已经发布了很长时间，但它引入的许多特性都没有得到充分利用，而且相当酷。下面是其中的三个。


### yield from


`yield` 关键字使 Python 更加强大。可以预见的是，人们都开始使用它来创建整个迭代器的生态系统。[itertools](https://docs.python.org/3/library/itertools.html) 模块和 [more-itertools](https://more-itertools.readthedocs.io/en/stable/) PyPI 包就是其中两个例子。


有时，一个新的生成器会想要使用一个现有的生成器。作为一个简单的（尽管有点故意设计）的例子，设想你想枚举所有的自然数对。


一种方法是按照“自然数对的和，自然数对的第一项”的顺序生成所有的自然数对。用 `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](https://opensource.com/article/18/7/setting-devpi) 服务器来管理内部软件包。对于 Parasol 的每个 Python 程序员来说，在 PyPI 上找一个未使用的名字是没有意义的，所以所有的内部包都被称为 `parasol.<business division>.<project>`。遵守最佳实践，开发人员希望包的名字能反映出这个命名系统。


这一点很重要！如果 `parasol.accounting.numeric_tricks` 包安装了一个名为 `numeric_tricks` 的顶层模块，这意味着依赖这个包的人将无法使用名为 `numeric_tricks` 的 PyPI 包，不管它写的有多好。


然而，这给开发者留下了一个两难的选择：哪个包拥有 `parasol/__init__.py` 文件？从 Python 3.3 开始，最好的解决办法是把 `parasol`，可能还有 `parasol.accounting`，变成没有 `__init__.py` 文件的 [命名空间包](https://www.python.org/dev/peps/pep-0420/)。


### 抑制异常的上下文


有时，在从异常中恢复的过程中出现的异常是一个问题，有上下文来跟踪它是很有用的。然而，有时却不是这样：异常已经被处理了，而新的情况是一个不同的错误状况。


例如，想象一下，在字典中查找一个键失败后，如果不能分析它，则希望失败并返回 `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 在十年前就已经发布了，但它的许多功能仍然很酷，而且没有得到充分利用。如果你还没有，就把它们添加到你的工具箱中吧。




---


via: <https://opensource.com/article/21/5/python-33>


作者：[Moshe Zadka](https://opensource.com/users/moshez) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出