在Python函数定义中,符号“->”表示函数返回类型。

946

最近我在查看Python 3.3语法规范时发现了一些有趣的东西:

funcdef: 'def' NAME parameters ['->' test] ':' suite

在 Python 2 中,可选的 'arrow' 块是不存在的,而且我找不到有关其在 Python 3 中含义的任何信息。事实证明,这是正确的 Python 代码,并被解释器接受:

def f(x) -> 123:
    return x

我本以为这可能是某种前提条件语法,但:

  • 我不能在这里测试x,因为它仍未定义,
  • 无论我在箭头后面放什么(例如2 < 1),都不会影响函数的行为。

熟悉这种语法风格的人能否解释一下?


1
如果你现在在任何代码中发现这个,那很可能是静态类型检查器(如mypy)的类型提示。https://www.mypy-lang.org/ - Joooeey
11个回答

702

这是一个函数注释

更详细地说,Python 2.x有docstrings,允许您将元数据字符串附加到各种类型的对象上。这非常方便,因此Python 3通过允许您为函数附加元数据来扩展了该功能,以描述其参数和返回值。

没有预先设定的用例,但PEP建议几个用途。一个非常方便的用途是允许您注释参数的预期类型;然后很容易编写一个装饰器,验证注释或将参数强制转换为正确的类型。另一个用途是允许参数特定的文档,而不是将其编码到docstring中。


186
信息可通过 .__annotations__ 属性获得。 - Martijn Pieters
15
哇,我错过了很广泛的知识领域 - 不仅是返回值注释,还有参数注释。非常感谢:)。 - Krotton
6
@Krotton 不能怪你没注意到它,因为它几乎没有被使用。我只遇到过一个使用它的图书馆,而且相当不知名。 - user395760
9
__annotations__ 属性是一个字典。其中键 return 用于在箭头后检索返回值。 - Keith
10
可能之所以很少人使用它,是因为大多数Python库仍然力求与Python 2.x兼容。随着Python 3.x变得更加普遍,我们可能会看到这种情况在各个地方变得更加普遍... - mgilson
显示剩余6条评论

617
这些是PEP 3107中涵盖的函数注释。具体来说,->表示返回函数注释。
示例:
def kinetic_energy(m:'in KG', v:'in M/S')->'Joules': 
    return 1/2*m*v**2
 
>>> kinetic_energy.__annotations__
{'return': 'Joules', 'v': 'in M/S', 'm': 'in KG'}

注释是字典,所以您可以这样做:

>>> '{:,} {}'.format(kinetic_energy(12,30),
      kinetic_energy.__annotations__['return'])
'5,400.0 Joules'

您可以使用Python数据结构而不仅仅是字符串:
rd={'type':float,'units':'Joules',
    'docstring':'Given mass and velocity returns kinetic energy in Joules'}
def f()->rd:
    pass

>>> f.__annotations__['return']['type']
<class 'float'>
>>> f.__annotations__['return']['units']
'Joules'
>>> f.__annotations__['return']['docstring']
'Given mass and velocity returns kinetic energy in Joules'

或者,您可以使用函数属性来验证调用的值:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        try: 
            pr=test.__name__+': '+test.__docstring__
        except AttributeError:
            pr=test.__name__   
        msg = '{}=={}; Test: {}'.format(var, value, pr)
        assert test(value), msg

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    _between.__docstring__='must be between {} and {}'.format(lo,hi)       
    return _between

def f(x: between(3,10), y:lambda _y: isinstance(_y,int)):
    validate(f, locals())
    print(x,y)

打印
>>> f(2,2) 
AssertionError: x==2; Test: _between: must be between 3 and 10
>>> f(3,2.1)
AssertionError: y==2.1; Test: <lambda>

非常有用,谢谢!虽然我记得下划线 _ 允许您调用最后一个函数/变量,但我想知道在这种情况下 _y 所代表的 lambda 是什么?此外,_between 可以被替换为 _ 吗? - Benjamin Diaz
1
在这两种情况下,_是一个快捷方式,表示“我想用与[那个]相同的名称,但我不想弄清楚命名空间是否允许我使用相同的名称或者会造成混淆。”使用between和内部函数_between时,它是不需要的,但如果使用相同的名称,则会产生混淆。这些可以是相同的名称。使用lambda表达式时,您有lambda函数的名称(字典的键)和参数的名称。同样,如果使用相同的名称,则会产生混淆。 - dawg

307
在以下代码中:
def f(x) -> int:
    return int(x)

-> int只是表明f()返回一个整数(但它并不强制函数返回一个整数)。它被称为返回注释,可以通过f.__annotations__['return']进行访问。

Python还支持参数注释:

def f(x: float) -> int:
    return int(x)
: float 告诉程序的读者以及一些第三方库/程序(例如pylint)x 应该是一个float。它可以通过 f.__annotations__['x'] 访问,本身没有任何意义。请参阅文档以获取更多信息:https://docs.python.org/3/reference/compound_stmts.html#function-definitionshttps://www.python.org/dev/peps/pep-3107/

131
如其他答案所述,->符号作为函数注释的一部分使用。在更近版本的Python>= 3.5中,它具有定义好的含义。 PEP 3107 -- 函数注释描述了规范,定义了语法上的变化,以及存在于func.__annotations__中的注释,并且其使用情况仍然开放。
然而,在Python 3.5中,PEP 484 -- 类型提示将单个含义附加到此处:->用于指示函数返回的类型。 此外,正如现有注释的情况处理中所述,这似乎将在未来版本中得到强制执行。
最快的方案将在3.6中引入对非类型提示注释的静默弃用,在3.7中完全弃用,并在Python 3.8中声明类型提示为注释的唯一允许使用方式。
(重点在于“Emphasis mine”)
据我所知,截至目前尚未在3.6中实际实施,因此可能会被推迟到未来版本。
根据这个,您提供的示例:
def f(x) -> 123:
    return x

在将来将被禁止(在当前版本中也会令人困惑),需要将其更改为:

def f(x) -> int:
    return x

为了有效地描述函数f返回类型为int的对象。

这些注释在Python本身中没有任何用处,它基本上只是填充并忽略它们。它取决于第三方库如何使用它们。


不错的回答。只是出于好奇,你在你的代码中使用它们吗? - PatrickT
1
AWS似乎正在使用它们:https://github.com/awslabs/fraud-detection-using-machine-learning/blob/master/source/env_setup.py - Bojan Komazec

14

这意味着函数返回的结果类型,但它可以是None

在面向Python 3.x的现代库中很普遍。

例如,在许多地方,比如pandas-profiling库的代码中都有它的存在:

def get_description(self) -> dict:

def get_rejected_variables(self, threshold: float = 0.9) -> list:

def to_file(self, output_file: Path or str, silent: bool = True) -> None:
"""Write the report to a file.

这意味着函数返回的结果类型,但它可以是None或任何其他类型。 - Ebram Shehata

10
def f(x) -> 123:
    return x

我的总结:

  1. 简单来说,-> 被引入是为了让开发者可选地指定函数的返回类型。参见Python Enhancement Proposal 3107

  2. 这表明随着 Python 的广泛应用,事物可能会如何发展 - 迈向强类型的迹象 - 这是我的个人观察。

  3. 还可以指定参数的类型。指定函数和参数的返回类型将有助于减少逻辑错误并改进代码的增强。

  4. 你可以在函数和参数级别上使用表达式作为返回类型,并且表达式的结果可以通过注释对象的 'return' 属性访问。注释 对于 lambda 内联函数的表达式/返回值将为空。


10
def f(x) -> str:
return x+4

print(f(45))

将会给出结果:49

换句话说,“-> str” 对返回类型没有影响:

print(f(45).__class__)

<class 'int'>

8

def function(arg)->123:

这是一个简单的返回类型,这里是整数,不管你写哪个数字。

就像Java一样:

public int function(int args){...}

但对于Python(正如Jim Fasarakis Hilliard所说的那样),返回类型仅仅是一个提示,因此它建议返回,但允许返回其他类型,如字符串...


6
->在Python3中被引入。
更简单的说,->后面的内容表示函数的返回类型。返回类型是可选的。

2
这个答案中的指导与已发布的先前答案中的指导有何不同? - Hoppeduppeanut
返回类型可能仍然不同,注释就像一个表达式,解释函数的返回值,但是如果我们在“->”后面放置str,但是返回int,Python将不会给出任何错误。 - typedecker

4

这只是告诉用户函数期望的输入或返回值。

funcname.__annotations__ 将打印详细信息。

例如:

def function(name:str ,age:int) -> "printing the personal details ":
    print(f"name is {name} age is {age}")

function("test",20)
print(function.__annotations__)

输出结果

name is test age is 20
{'name': <class 'str'>, 'age': <class 'int'>, 'return': 'printing the personal details '}

即使你返回数值,它也不会显示任何内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接