Python 语法中有一些元素不太常见,也很少用到。这是因为它们能提供的好处很少,

或者它们的用法很难记住。因此,许多 Python 程序员(即使有多年的经验)完全不知道这

些语法元素的存在。其中最有名的例子如下:

• for ... else 语句。

• 函数注解(function annotation)。

for...else...语句

在 for 循环之后使用 else 子句,可以在循环“自然”结束而不是被 break 语句终

止时执行一个代码块:

>>> for number in range(1):

... break

... else:

... print("no break")

...

>>>

>>> for number in range(1):

... pass

... else:

... print("break")

...

break

这一语句在某些情况下很有用,因为它有助于删除一些“哨兵(sentinel)”变量,如果

出现 break 时用户想要保存信息,可能会需要这些变量。这使得代码更加清晰,但可能会

使不熟悉这种语法的程序员感到困惑。有人说 else 子句的这种含义是违反直觉的,但这

里介绍一个简单的技巧,可以帮你记住它的用法:for 循环之后 else 子句的含义是“没

有 break”。

函数注解

函数注解是 Python 3 最独特的功能之一。官方文档是这么说的:函数注解是关于用户

自定义函数使用的类型的完全可选的元信息,但事实上,它并不局限于类型提示,而且在

Python 及其标准库中也没有单个功能可以利用这种注解。这就是这个功能独特的原因:它

没有任何语法上的意义。可以为函数定义注解,并在运行时获取这些注解,但仅此而已。

如何使用注解留给开发人员去思考。

1.一般语法

对 Python 官方文档中的示例稍作修改,就可以很好展示如何定义并获取函数注解:

>>> def f(ham: str, eggs: str = 'eggs') -> str:

... pass

...

>>> print(f.__annotations__)

{'return': <class 'str'>, 'eggs': <class 'str'>, 'ham': <class 'str'>}

如上所述,参数注解的定义为冒号后计算注解值的表达式。返回值注解的定义为表示

def 语句结尾的冒号与参数列表之后的->之间的表达式。

定义好之后,注解可以通过函数对象的__annotations__属性获取,它是一个字典,

在应用运行期间可以获取。

任何表达式都可以用作注解,其位置靠近默认参数,这样可以创建一些迷惑人的函数

定义,如下所示:

>>> def square(number: 0<=3 and 1=0) -> (\

... +9000): return number**2

>>> square(10)

100

不过,注解的这种用法只会让人糊涂,没有任何其他作用。即使不用注解,编写出难

以阅读和理解的代码也是相对容易的。

2.可能的用法

虽然注解有很大的潜力,但并没有被广泛使用。一篇介绍 Python 3 新增功能的文章(参见

https://docs.python.org/3/whatsnew/3.0.html)称,此功能的目的是“鼓励通过元类、装饰器或框架

进行试验”。另一方面,作为提议函数注解的官方文档,PEP 3107 列出以下可能的使用场景:

• 提供类型信息。

○ 类型检查。

○ 让 IDE 显示函数接受和返回的类型。

○ 函数重载/通用函数。

○ 与其他语言之间的桥梁。

○ 适配。

○ 谓词逻辑函数。

○ 数据库查询映射。

○ RPC 参数编组。

• 其他信息。

○ 参数和返回值的文档。

虽然函数注解存在的时间和 Python 3 一样长,但仍然很难找到任一常见且积极维护的

包,将函数注解用作类型检查之外的功能。所以函数注解仍主要用于试验和玩耍,这也是

Python 3 最初发布时包含该功能的最初目的。

小结

本章介绍了不直接与 Python 类和面向对象编程相关的多个最佳语法实践。本章第一部

分重点介绍了与 Python 序列和集合相关的语法特性,也讨论了字符串和字节相关的序列。

本章其余部分介绍了两组独立的语法元素:一组是初学者相对难以理解的(例如迭代器、

生成器和装饰器),另一组是鲜为人知的(for...else 子句和函数注解)。

子类化内置类型

Python 的子类化内置类型非常简单。有一个叫作 object 的内置类型,它是所有内置

类型的共同祖先,也是所有没有显式指定父类的用户自定义类的共同祖先。正由于此,每

当需要实现与某个内置类型具有相似行为的类时,最好的方法就是将这个内置类型子类化。

现在,我们将向你展示一个名为 distinctdict 类的代码如下,它就使用了这种方

法。它是 Python 中普通的 dict 类型的子类。这个新类的大部分行为都与普通的 dict 相

同,但它不允许多个键对应相同的值。如果有人试图添加具有相同值的新元素,那么会引

发一个 ValueError 的子类,并给出一些帮助信息:

class DistinctError(ValueError):

"""如果向 distinctdict 添加重复值,则引发这个错误。"""

class distinctdict(dict):

"""不接受重复值的字典。"""

def __setitem__(self, key, value):

if value in self.values():

if (

(key in self and self[key] != value) or

key not in self

):

raise DistinctError(

"This value already exists for different key"

)

super().__setitem__(key, value)

下面是在交互式会话中使用 distinctdict 的示例:

>>> my = distinctdict()

>>> my['key'] = 'value'

>>> my['other_key'] = 'value'

Traceback (most recent call last):

File "<input>", line 1, in <module>

File "<input>", line 10, in __setitem__

DistinctError: This value already exists for different key

>>> my['other_key'] = 'value2'

>>> my

{'key': 'value', 'other_key': 'value2'}

如果查看现有代码,你可能会发现许多类都是对内置类型的部分实现,它们作为子类

的速度更快,代码更整洁。举个例子,list 类型用来管理序列,如果一个类需要在内部

处理序列,那么就可以对 list 进行子类化,如下所示:

class Folder(list):

def __init__(self, name):

self.name = name

def dir(self, nesting=0):

offset = " " * nesting

print('%s%s/' % (offset, self.name))

for element in self:

if hasattr(element, 'dir'):

element.dir(nesting + 1)

else:

print("%s %s" % (offset, element))

下面是在交互式会话中的使用示例:

>>> tree = Folder('project')

>>> tree.append('README.md')

>>> tree.dir()

project/

README.md

>>> src = Folder('src')

>>> src.append('script.py')

>>> tree.append(src)

>>> tree.dir()

project/

README.md

src/

script.py