3 个值得使用的首次亮相在 Python 3.0 中的特性
Python

> 
> 探索一些未被充分利用但仍然有用的 Python 特性。
> 
> 
> 


![](/data/attachment/album/202105/20/103117me72dllr6lebk1fv.jpg "Hands on a keyboard with a Python book ")


这是 Python 3.x 首发特性系列文章的第一篇。Python 3.0 于 2008 年首次发布，尽管它已经发布了一段时间，但它引入的许多特性都没有被充分利用，而且相当酷。这里有三个你应该知道的。


### 仅限关键字参数


Python 3.0 首次引入了**仅限关键字参数**参数的概念。在这之前，不可能指定一个只通过关键字传递某些参数的 API。这在有许多参数，其中一些参数可能是可选的函数中很有用。


请看一个特意设计的例子：



```
def show_arguments(base, extended=None, improved=None, augmented=None):
    print("base is", base)
    if extended is not None:
        print("extended is", extended)
    if improved is not None:
        print("improved is", improved)
    if augmented is not None:
        print("augmented is", augmented)

```

当阅读调用该函数的代码时，有时很难理解发生了什么：



```
show_arguments("hello", "extra")

    base is hello
    extended is extra

show_arguments("hello", None, "extra")

    base is hello
    improved is extra

```

虽然可以用关键字参数来调用这个函数，但这明显不是最好的方法。相反，你可以将这些参数标记为仅限关键字：



```
def show_arguments(base, *, extended=None, improved=None, augmented=None):
    print("base is", base)
    if extended is not None:
        print("extended is", extended)
    if improved is not None:
        print("improved is", improved)
    if augmented is not None:
        print("augmented is", augmented)

```

现在，你不能用位置参数传入额外的参数：



```
show_arguments("hello", "extra")
    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-7-6000400c4441> in <module>
    ----> 1 show_arguments("hello", "extra")
   

    TypeError: show_arguments() takes 1 positional argument but 2 were given

```

对该函数的有效调用更容易预测：



```
show_arguments("hello", improved="extra")
    base is hello
    improved is extra

```

### nonlocal


有时，函数式编程的人根据编写累加器的难易程度来判断一种语言。累加器是一个函数，当它被调用时，返回目前为止发给它的所有参数的总和。


在 3.0 之前，Python 的标准答案是：



```
class _Accumulator:
    def __init__(self):
        self._so_far = 0
    def __call__(self, arg):
        self._so_far += arg
        return self._so_far

def make_accumulator():
    return _Accumulator()

```

虽然我承认有些啰嗦，但这确实有效：



```
acc = make_accumulator()
print("1", acc(1))
print("5", acc(5))
print("3", acc(3))

```

这样做的输出结果将是：



```
1 1
5 6
3 9

```

在 Python 3.x 中，`nonlocal` 关键字可以用少得多的代码实现同样的行为。



```
def make_accumulator():
    so_far = 0
    def accumulate(arg):
        nonlocal so_far
        so_far += arg
        return so_far
    return accumulate

```

虽然累加器是人为的例子，但使用 `nonlocal` 关键字使内部函数拥有具有状态的的能力是一个强大的工具。


### 扩展析构


想象一下，你有一个 CSV 文件，每一行由几个元素组成：


* 第一个元素是年份
* 第二个元素是月
* 其他元素是该月发表的全部文章数，每天一个条目


请注意，最后一个元素是 *文章总数*，而不是 *每天发表的文章*。例如，一行的开头可以是：



```
2021,1,5,8,10

```

这意味着在 2021 年 1 月，第一天发表了 5 篇文章。第二天，又发表了三篇文章，使总数达到 8 篇。第三天，又发表了两篇文章。


一个月可以有 28 天、30 天或 31 天。提取月份、日期和文章总数有多难？


在 3.0 之前的 Python 版本中，你可能会这样写：



```
year, month, total = row[0], row[1], row[-1]

```

这是正确的，但它掩盖了格式。使用**扩展析构**，同样可以这样表达：



```
year, month, *rest, total = row

```

这意味着如果该格式改为前缀了一个描述，你可以把代码改成：



```
_, year, month, *rest, total = row

```

而不需要在每个索引中添加 `1`。


### 接下来是什么？


Python 3.0 和它的后期版本已经推出了 12 年多，但是它的一些功能还没有被充分利用。在本系列的下一篇文章中，我将会写另外三个。




---


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


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


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


OK
This is the first in a series of articles about features that first appeared in a version of Python 3.x. Python 3.0 was first released in 2008, and even though it has been out for a while, many of the features it introduced are underused and pretty cool. Here are three you should know about.

## Keyword-only arguments

Python 3.0 first introduced the idea of **keyword-only** arguments. Before this, it was impossible to specify an API where some arguments could be passed in only via keywords. This is useful in functions with many arguments, some of which might be optional.

Consider a contrived example:

```
def show_arguments(base, extended=None, improved=None, augmented=None):
print("base is", base)
if extended is not None:
print("extended is", extended)
if improved is not None:
print("improved is", improved)
if augmented is not None:
print("augmented is", augmented)
```


When reading code that calls this function, it is sometimes hard to understand what is happening:

`show_arguments("hello", "extra")`


```
base is hello
extended is extra
```


`show_arguments("hello", None, "extra")`


```
base is hello
improved is extra
```


While it is possible to call this function with keyword arguments, it is not obvious that this is the best way. Instead, you can mark these arguments as keyword-only:

```
def show_arguments(base, *, extended=None, improved=None, augmented=None):
print("base is", base)
if extended is not None:
print("extended is", extended)
if improved is not None:
print("improved is", improved)
if augmented is not None:
print("augmented is", augmented)
```


Now, you can't pass in the extra arguments with positional arguments:

`show_arguments("hello", "extra")`


```
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-7-6000400c4441> in <module>
----> 1 show_arguments("hello", "extra")
TypeError: show_arguments() takes 1 positional argument but 2 were given
```


Valid calls to the function are much easier to predict:

`show_arguments("hello", improved="extra")`


```
base is hello
improved is extra
```


## nonlocal

Sometimes, functional programming folks judge a language by how easy is it to write an accumulator. An accumulator is a function that, when called, returns the sum of all arguments sent to it so far.

The standard answer in Python before 3.0 was:

```
class _Accumulator:
def __init__(self):
self._so_far = 0
def __call__(self, arg):
self._so_far += arg
return self._so_far
def make_accumulator():
return _Accumulator()
```


While admittedly somewhat verbose, this does work:

```
acc = make_accumulator()
print("1", acc(1))
print("5", acc(5))
print("3", acc(3))
```


The output for this would be:

```
1 1
5 6
3 9
```


In Python 3.x, **nonlocal** can achieve the same behavior with significantly less code.

```
def make_accumulator():
so_far = 0
def accumulate(arg):
nonlocal so_far
so_far += arg
return so_far
return accumulate
```


While accumulators are contrived examples, the ability to use the `nonlocal`

keyword to have inner functions with state is a powerful tool.

## Extended destructuring


Imagine you have a CSV file where each row consists of several elements:

- The first element is a year
- The second element is a month
- The other elements are the total articles published that month, one entry for each day

Note that the last element is *total articles*, not *articles published per day*. For example, a row can begin with:

`2021,1,5,8,10`


This means that in January 2021, five articles were published on the first day. On the second day, three more articles were published, bringing the total to 8. On the third day, two more articles were published.

Months can have 28, 30, or 31 days. How hard is it to extract the month, day, and total articles?

In versions of Python before 3.0, you might write something like:

`year, month, total = row[0], row[1], row[-1]`


This is correct, but it obscures the format. With **extended destructuring**, the same can be expressed this way:

`year, month, *rest, total = row`


This means that if the format ever changes to prefix a description, you can change the code to:

`_, year, month, *rest, total = row`


Without needing to add `1`

to each of the indices.

## What's next?

Python 3.0 and its later versions have been out for more than 12 years, but some of its features are underutilized. In the next article in this series, I'll look at three more of them.

## Comments are closed.