33

我已经浏览了 PEP 3107 中的前几节,但我仍然不明白它们对语言有什么好处。在我看来,您可以使用装饰器将元数据添加到函数中。例如

def returns(return_type):
  f.return_type = return_type  # <- adding metadata here
  return f

@returns(Foo)
def make_foo(): ...

您也可以将元数据添加到参数中,如果您利用默认参数,它看起来会很漂亮,如下所示:

import inspect

def defaults_are_actually_metadata(f):
  names, args_name, kwargs_name, defaults = inspect.getfuncspec(f)
  f.parameter_metadata = dict(zip(names[-len(defaults):], defaults))
  f.__defaults__ = ()
  return f

@defaults_are_actually_metadata
def haul(load="Stuff to be carried.",
         speed_mph="How fast to move the load (in miles per hour)."): ...

至少我最初的印象是注解是多余的:装饰器可以做所有注解可以做的事情(甚至更多)。在向函数添加元数据时,为什么注释比装饰器更好?

4

3 回答 3

32

正如您所提到的,相关的 PEP 是3107(链接以便于参考,以防遇到此问题的其他人尚未阅读)。

目前,注释是一种实验,一种正在进行中的工作。实际上,python-ideas 邮件列表中最近有一个关于该主题的线程可能会有所帮助。(提供的链接仅用于每月存档;我发现特定帖子的 URL 往往会定期更改。有问题的线程在 12 月初附近,标题为“[Python-ideas] 函数注释约定”。第一篇文章来自 Thomas Kluyver 于 12 月 1 日。)

以下是 Guido van Rossum 在该线程中的一篇帖子:

Jasper St. Pierre 在 2012 年 12 月 4 日上午 11:43 写道:

确实。我以前看过注释,但我从来不明白它的目的。这似乎是一个在设计和实施时没有考虑任何目标的功能,社区应该自己发现目标。

圭多的回应:

从相反的方面来说。有太多的用例立即看起来很重要,我们不知道哪些是最重要的或如何组合它们,所以我们决定采取两步法:在第一步中,我们设计了语法,而在第 2 步中,我们将设计语义。这个想法非常明确,一旦语法确定下来,人们就可以自由地尝试不同的语义——只是不在标准库中。这个想法也是,最终,从所有这些实验中,会出现一个适合标准库的实验。

贾斯珀圣皮埃尔:

那么,如果我可能会问,注释的最初目标是什么?PEP 给出了一些建议,但没有留下任何具体内容。它是为 IDE 或检查源代码的静态分析工具而设计的吗?应用程序本身可以通过什么来提供特殊行为,例如命令行解析器或运行时静态检查器?

圭多的回应:

在某种程度上,几乎所有上述内容。但对我个人而言,主要目标始终是为参数和返回值指定类型约束(可能还有其他约束)。我在不同的时间都在玩弄组合类型的特定方法。例如 list[int] 可能意味着一个整数列表,而 dict[str, tuple[float, float, float, bool]] 可能意味着一个 dict 将字符串映射到三个浮点数和一个布尔值的元组。但我觉得就这样的符号达成共识要比对参数注释的语法达成共识要困难得多(想想你可以对这两个例子提出多少反对意见:-)——我一直有强烈的使用欲望"var: type = default" 并使该类型成为与默认值同时评估的运行时表达式。

还有一点来自 Ned Batchelder 的幽默:

对我来说,一个有说服力的时刻是在 PyCon 的早期 Py3k 主题演讲中(可能是在达拉斯或芝加哥?),Guido 不记得“注释”这个词,并说,“你知道,那些不是类型声明的东西?” :-)

于 2012-12-09T15:19:47.510 回答
8

我认为第一段说明了一切:

因为 Python 的 2.x 系列缺少注释函数参数和返回值的标准方式

(强调我的)

使用标准方法执行此操作的好处是您可以准确地知道注释的位置。

至于您关于有另一种方法的论点,您可以对列表推导提出相同的论点:

out = []
for x in my_iterable:
    out.append(x)
于 2012-12-09T05:06:51.627 回答
6

他们有两个不同的角色。

注释是参数的文档/注释,而装饰器转换函数。

Python 本身并不赋予注解任何特定的含义或意义。

Python 装饰器页面

装饰器动态地改变函数、方法或类的功能,而无需直接使用子类或更改被装饰函数的源代码。

于 2012-12-09T06:51:22.763 回答