O que significa -> nas definições de função Python?

476

Recentemente, notei algo interessante ao analisar a especificação gramatical do Python 3.3 :

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

O bloco opcional 'arrow' estava ausente no Python 2 e não consegui encontrar nenhuma informação sobre seu significado no Python 3. Acontece que este é o Python correto e é aceito pelo intérprete:

def f(x) -> 123:
    return x

Eu pensei que isso poderia ser algum tipo de sintaxe de pré-condição, mas:

  • Eu não posso testar xaqui, ainda está indefinido,
  • Não importa o que eu coloquei depois da seta (por exemplo 2 < 1), ela não afeta o comportamento da função.

Alguém acostumado com essa sintaxe pode explicar isso?

Krotton
fonte

Respostas:

375

É uma anotação de função .

Mais detalhadamente, o Python 2.x possui docstrings, que permitem anexar uma string de metadados a vários tipos de objetos. Isso é incrivelmente útil, então o Python 3 estende o recurso, permitindo anexar metadados às funções que descrevem seus parâmetros e retornam valores.

Não há caso de uso preconcebido, mas o PEP sugere vários. Um muito útil é permitir que você anote parâmetros com seus tipos esperados; seria fácil escrever um decorador que verifique as anotações ou force os argumentos para o tipo correto. Outra é permitir a documentação específica do parâmetro em vez de codificá-la na docstring.

Katriel
fonte
122
E a informação está disponível como um .__annotations__atributo.
Martijn Pieters
8
Uau, eu perdi uma área de conhecimento bastante ampla - não apenas as anotações de valor de retorno, mas também as anotações de parâmetro. Muito obrigado :).
precisa saber é o seguinte
4
@Krotton Não posso culpar você por perdê-lo, é praticamente não utilizado. Eu só conheci uma única biblioteca usando-os, e é bastante obscuro.
5
E o __annotations__atributo é um dicionário. A chave returné aquela usada para recuperar o valor após a seta.
Keith
9
@ delnan - provavelmente o motivo pelo qual não é mais utilizado é porque a maioria das bibliotecas python ainda procura ser compatível com python2.x. À medida que o python3.x começa a se tornar mais padrão, podemos ver mais dessas coisas surgindo aqui e ali ...
mgilson
252

Estas são anotações de função cobertas no PEP 3107 . Especificamente, ->marca a anotação da função de retorno.

Exemplos:

>>> 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'}

As anotações são dicionários, para que você possa fazer isso:

>>> '{:,} {}'.format(kinetic_energy(20,3000),
      kinetic_energy.__annotations__['return'])
'90,000,000.0 Joules'

Você também pode ter uma estrutura de dados python em vez de apenas uma string:

>>> 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'

Ou, você pode usar atributos de função para validar os valores chamados:

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)

Impressões

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

Como outras respostas indicaram, o ->símbolo é usado como parte das anotações de função. Nas versões mais recentes do Python >= 3.5, porém, ele tem um significado definido .

O PEP 3107 - Anotações de Função descreveu a especificação, definindo as alterações gramaticais, a existência func.__annotations__na qual elas são armazenadas e o fato de seu caso de uso ainda estar aberto.

No 3.5entanto, em Python , o PEP 484 - Type Hints atribui um único significado a isso: ->é usado para indicar o tipo que a função retorna. Também parece que isso será aplicado em versões futuras, conforme descrito em E sobre os usos existentes das anotações :

O esquema mais rápido concebível introduziria a descontinuação silenciosa de anotações que não são de tipo dica em 3.6, a depreciação total em 3.7 e declararia as dicas de tipo como o único uso permitido de anotações no Python 3.8.

(Ênfase minha)

Na verdade, isso não foi implementado, 3.6até onde posso dizer, para que possa ter problemas com versões futuras.

De acordo com isso, o exemplo que você forneceu:

def f(x) -> 123:
    return x

será proibido no futuro (e nas versões atuais serão confusas), ele precisará ser alterado para:

def f(x) -> int:
    return x

por isso para descrever de forma eficaz que os função fretorna um objecto do tipo int.

As anotações não são usadas de forma alguma pelo próprio Python, elas as preenchem e as ignoram. Cabe a bibliotecas de terceiros trabalhar com elas.

Dimitris Fasarakis Hilliard
fonte
64

No código a seguir:

def f(x) -> int:
    return int(x)

o -> intjust diz que f()retorna um número inteiro (mas não força a função a retornar um número inteiro). É chamado de anotação de retorno e pode ser acessado como f.__annotations__['return'].

O Python também suporta anotações de parâmetro:

def f(x: float) -> int:
    return int(x)

: floatinforma às pessoas que leem o programa (e algumas bibliotecas / programas de terceiros, por exemplo, pylint) que xdeve ser a float. É acessado como f.__annotations__['x']e não tem nenhum significado por si só. Veja a documentação para mais informações:

https://docs.python.org/3/reference/compound_stmts.html#function-definitions https://www.python.org/dev/peps/pep-3107/

MaxiMouse
fonte
4

Isso significa o tipo de resultado que a função retorna, mas pode ser None.

É difundido em bibliotecas modernas orientadas no Python 3.x.

Por exemplo, existe no código da biblioteca pandas-profiling em muitos lugares, por exemplo:

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.
Vitalii
fonte
"Isso significa o tipo de resultado que a função retorna, mas pode ser Nenhum." Pode ser Nenhum ou qualquer outro tipo.
Ebram Shehata
2

def function(arg)->123:

É simplesmente um tipo de retorno, número inteiro , neste caso, não importa qual número você escreve.

como Java :

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

Mas, para Python (como Jim Fasarakis Hilliard disse), o tipo de retorno é apenas uma dica , por isso é sugerido o retorno, mas permita de qualquer maneira retornar outro tipo como uma string.

Mike D3ViD Tyson
fonte
1
def f(x) -> 123:
    return x

Meu resumo:

  1. O Simply ->é apresentado para que os desenvolvedores especifiquem opcionalmente o tipo de retorno da função. Consulte a Proposta de aprimoramento do Python 3107

  2. Esta é uma indicação de como as coisas podem se desenvolver no futuro, à medida que o Python for adotado extensivamente - uma indicação para digitação forte - esta é minha observação pessoal.

  3. Você também pode especificar tipos para argumentos. A especificação do tipo de retorno das funções e argumentos ajudará a reduzir erros lógicos e a aprimorar aprimoramentos de código.

  4. Você pode ter expressões como tipo de retorno (para os níveis de função e parâmetro) e o resultado das expressões pode ser acessado através do atributo 'return' do objeto de anotações . as anotações ficarão vazias para o valor de expressão / retorno para funções inline lambda.

maz
fonte
Obrigado pelas correções.
maz