是否可以在Python的函数注释中引用函数参数?

时间:2018-07-11 11:36:54

标签: python annotations type-systems pep

我想说

def f(param) -> type(param): return param

但是我得到了NameError: name 'param' is not defined。这里的关键是返回类型是函数参数的函数。我浏览了https://www.python.org/dev/peps/pep-3107/,但看不到任何有关有效注释表达式的精确描述。

我会接受一个答案,解释为什么现在完全是不可能的,即,它是否不适合当前的注释范例,或者这是否存在技术问题?

2 个答案:

答案 0 :(得分:1)

type(param)方法存在一些问题。

首先,正如Oleh在他的回答中提到的那样,所有注释必须在函数定义时有效。在像您这样的示例中,由于variable shadowing,您可能会遇到问题。

param = 10

def f(param) -> type(param):
    return param

f('a')

由于变量 param的类型为int,因此该函数的注释实质上被读为f(param: Any) -> int。因此,当您使用值param传递自变量 'a'时,这意味着f将返回str,这使其与注解。诚然,这个示例是人为设计的,但是从语言设计的角度来看,这是要小心的事情。

相反,正如jonrsharpe所述,引用generic types来引用参数type variables(如jonrsharpe)的最佳方法通常是使用abstract base class

这可以使用typing.TypeVar类来完成。

from typing import TypeVar

def f(param: T) -> T:
    return param

这意味着静态检查器将不需要实际访问param的类型,只需检查一下检查时是否有一种方法可以同时考虑param和返回值。相同的类型。我说的是同一类型,因为有时您只会断言它们都实现了相同的numbers.Real /接口,例如type variables

然后可以在泛型类型中使用typevars

from typing import List, TypeVar

T = TypeVar('T')

def total(items: List[T]) -> List[T]:
    return [f(item) for item in items]

使用类型变量和泛型会更好,因为它添加了更多信息,并提供了更多的灵活性(如示例中的numbers.Real所述)。例如,使用List[T]的能力非常重要。在使用type(param)的情况下,它只会返回list,而不像List[T]那样返回的列表。因此,使用type(param)实际上会丢失信息,而不是添加它。

因此,最好还是使用generic typesvariable shadowing

TL; DR

  1. 由于abstract base classestype(param)可能导致注释不一致。
  2. 由于有时在考虑系统类型时,您会考虑使用接口(Python中的type variables)而不是具体类型,因此最好依靠ABC和documentation
  3. 使用type(param)可能会丢失通用信息会提供的信息

答案 1 :(得分:0)

让我们看一下https://122e4e-mapbox.global.ssl.fastly.net/mapbox-gl-js/example/polygon-popup-on-click/

  

注释必须是有效的表达式,其值在定义函数时不会引起异常 (但有关正向引用,请参见下文)。

     

注释应保持简单,否则静态分析工具可能无法解释这些值。例如,动态计算的类型不太可能被理解。 (这是一个故意含糊的要求,经讨论保证,可以在此PEP的将来版本中添加特定的包含和排除。)

我想说您的方法很有趣,可能对静态分析很有用。但是,如果我们接受PEP作为当前注释范例解释的来源,则突出显示的文本将说明为什么在调用时无法动态定义返回类型的原因。