回顾一下 Python 3.4 中的枚举
Python

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


![](/data/attachment/album/202105/30/230947j19r2772m12tccrh.jpg "old school calculator")


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


### 枚举


我最喜欢的逻辑谜题之一是自我描述的 [史上最难的逻辑谜题](https://en.wikipedia.org/wiki/The_Hardest_Logic_Puzzle_Ever)。在其中，它谈到了三个“神”，他们被称为 A、B 和 C，他们的身份是真、假和随机，按一定顺序排列。你可以问他们问题，但他们只用神的语言回答，其中 “da” 和 “ja” 表示 “是” 和 “不是”，但你不知道哪个是哪个。


如果你决定使用 Python 来解决这个问题，你将如何表示神的名字和身份以及神的语言中的词语？传统的答案是使用字符串。然而，字符串的拼写错误可能会带来灾难性的后果。


如果在解题的关键部分，你用字符串 “jaa” 而不是 “ja” 进行比较，你就会得到一个错误的答案。虽然谜题没有说明风险是什么，但这可能是最好的避免方式。


`enum` 模块让你能够以一种可调试但安全的方式来定义这些东西：



```
import enum

@enum.unique
class Name(enum.Enum):
    A = enum.auto()
    B = enum.auto()
    C = enum.auto()
   
@enum.unique
class Identity(enum.Enum):
    RANDOM = enum.auto()
    TRUE = enum.auto()
    FALSE = enum.auto()

       
@enum.unique
class Language(enum.Enum):
    ja = enum.auto()
    da = enum.auto()

```

枚举的一个好处是，在调试日志或异常中，枚举的呈现方式是有帮助的：



```
name = Name.A
identity = Identity.RANDOM
answer = Language.da
print("I suspect", name, "is", identity, "because they answered", answer)

```


```
    I suspect Name.A is Identity.RANDOM because they answered Language.da

```

### functools.singledispatch


在开发游戏的“基础设施”层时，你想通用地处理各种游戏对象，但仍然允许这些对象自定义动作。为了使这个例子更容易解释，假设这是一个基于文本的游戏。当你使用一个对象时，大多数情况下，它只会打印 `You are using <x>`。但是使用一把特殊的剑可能需要随机滚动，否则会失败。


当你获得一个物品时，它通常会被添加到库存中。然而，一块特别重的石头会砸碎一个随机物品。如果发生这种情况，库存中会失去该物体。


处理这个问题的一个方法是在物品上设置 `use` 和 `acquire` 方法。随着游戏复杂性的增加，这些方法会越来越多，使游戏对象变得难以编写。


相反，`functools.singledispatch` 允许你以安全和尊重命名空间的方式追溯性地添加方法。


你可以定义没有行为的类：



```
class Torch:
    name="torch"

class Sword:
    name="sword"

class Rock:
    name="rock"

```


```
import functools

@functools.singledispatch
def use(x):
    print("You use", x.name)

@functools.singledispatch
def acquire(x, inventory):
    inventory.add(x)

```

对于火炬来说，这些通用的实现已经足够了：



```
inventory = set()

def deploy(thing):
    acquire(thing, inventory)
    use(thing)
    print("You have", [item.name for item in inventory])

deploy(Torch())

```


```
    You use torch
    You have ['torch']

```

然而，剑和石头需要一些专门的功能：



```
import random

@use.register(Sword)
def use_sword(sword):
    print("You try to use", sword.name)
    if random.random() < 0.9:
        print("You succeed")
    else:
        print("You fail")

deploy(sword)

```


```
    You try to use sword
    You succeed
    You have ['sword', 'torch']

```


```
import random

@acquire.register(Rock)
def acquire_rock(rock, inventory):
    to_remove = random.choice(list(inventory))
    inventory.remove(to_remove)
    inventory.add(rock)

deploy(Rock())

```


```
    You use rock
    You have ['sword', 'rock']

```

岩石可能压碎了火炬，但你的代码更容易阅读。


### pathlib


从一开始，Python 中文件路径的接口就是“智能字符串操作”。现在，通过 `pathlib`，Python 有了一种面向对象的方法来操作路径。



```
import pathlib

```


```
gitconfig = pathlib.Path.home() / ".gitconfig"
text = gitconfig.read_text().splitlines()

```

诚然，用 `/` 作为操作符来生成路径名有点俗气，但在实践中却不错。像 `.read_text()` 这样的方法允许你从小文件中获取文本，而不需要手动打开和关闭文件句柄。


这使你可以集中精力处理重要的事情：



```
for line in text:
    if not line.strip().startswith("name"):
        continue
    print(line.split("=")[1])

```


```
     Moshe Zadka

```

### 欢迎来到 2014 年


Python 3.4 大约在七年前就发布了，但是在这个版本中首次出现的一些功能非常酷，而且没有得到充分利用。如果你还没使用，那么将他们添加到你的工具箱中。




---


via: <https://opensource.com/article/21/5/python-34-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 fifth in a series of articles about features that first appeared in a version of Python 3.x. Python 3.4 was first released in 2014, and even though it has been out for a long time, many of the features it introduced are underused and pretty cool. Here are three of them.

## enum


One of my favorite logic puzzles is the self-descriptive [Hardest Logic Puzzle Ever](https://en.wikipedia.org/wiki/The_Hardest_Logic_Puzzle_Ever). Among other things, it talks about three gods who are called A, B, and C. Their identities are True, False, and Random, in some order. You can ask them questions, but they only answer in the god language, where "da" and "ja" mean "yes" and "no," but you do not know which is which.

If you decide to use Python to solve the puzzle, how would you represent the gods' names and identities and the words in the god language? The traditional answer has been to use strings. However, strings can be misspelled with disastrous consequences.

If, in a critical part of your solution, you compare to the string `jaa`

instead of `ja`

, you will have an incorrect solution. While the puzzle does not specify what the stakes are, that's probably best avoided.

The `enum`

module gives you the ability to define these things in a debuggable yet safe manner:

```
import enum
@enum.unique
class Name(enum.Enum):
A = enum.auto()
B = enum.auto()
C = enum.auto()
@enum.unique
class Identity(enum.Enum):
RANDOM = enum.auto()
TRUE = enum.auto()
FALSE = enum.auto()
@enum.unique
class Language(enum.Enum):
ja = enum.auto()
da = enum.auto()
```


One advantage of enums is that in debugging logs or exceptions, the enum is rendered helpfully:

```
name = Name.A
identity = Identity.RANDOM
answer = Language.da
print("I suspect", name, "is", identity, "because they answered", answer)
```


` I suspect Name.A is Identity.RANDOM because they answered Language.da`


## functools.singledispatch

While developing the "infrastructure" layer of a game, you want to deal with various game objects generically but still allow the objects to customize actions. To make the example easier to explain, assume it's a text-based game. When you use an object, most of the time, it will just print `You are using <x>`

. But using a special sword might require a random roll, and it will fail otherwise.

When you acquire an object, it is usually added to the inventory. However, a particularly heavy rock will smash a random object; if that happens, the inventory will lose that object.

One way to approach this is to have methods `use`

and `acquire`

on objects. More and more of these methods will be added as the game's complexity increases, making game objects unwieldy to write.

Instead, `functools.singledispatch`

allows you to add methods retroactively—in a safe and namespace-respecting manner.

You can define classes with no behavior:

```
class Torch:
name="torch"
class Sword:
name="sword"
class Rock:
name="rock"
```


```
import functools
@functools.singledispatch
def use(x):
print("You use", x.name)
@functools.singledispatch
def acquire(x, inventory):
inventory.add(x)
```


For the torch, those generic implementations are enough:

```
inventory = set()
def deploy(thing):
acquire(thing, inventory)
use(thing)
print("You have", [item.name for item in inventory])
deploy(Torch())
```


```
You use torch
You have ['torch']
```


However, the sword and the rock need some specialized functionality:

```
import random
@use.register(Sword)
def use_sword(sword):
print("You try to use", sword.name)
if random.random() < 0.9:
print("You succeed")
else:
print("You fail")
deploy(sword)
```


```
You try to use sword
You succeed
You have ['sword', 'torch']
```


```
import random
@acquire.register(Rock)
def acquire_rock(rock, inventory):
to_remove = random.choice(list(inventory))
inventory.remove(to_remove)
inventory.add(rock)
deploy(Rock())
```


```
You use rock
You have ['sword', 'rock']
```


The rock might have crushed the torch, but your code is much easier to read.

## pathlib

The interface to file paths in Python has been "smart-string manipulation" since the beginning of time. Now, with `pathlib`

, Python has an object-oriented way to manipulate paths:

`import pathlib`


```
gitconfig = pathlib.Path.home() / ".gitconfig"
text = gitconfig.read_text().splitlines()
```


Admittedly, using `/`

as an operator to generate path names is a little cutesy, but it ends up being nice in practice. Methods like `.read_text()`

allow you to get text out of small files without needing to open and close file handles manually.

This lets you concentrate on the important stuff:

```
for line in text:
if not line.strip().startswith("name"):
continue
print(line.split("=")[1])
```


` Moshe Zadka`


## Welcome to 2014

Python 3.4 was released about seven years ago, but some of the features that first showed up in this release are cool—and underused. Add them to your toolkit if you haven't already.

## Comments are closed.