Co -> oznacza w definicjach funkcji Pythona?

475

Niedawno zauważyłem coś interesującego, patrząc na specyfikację gramatyki Python 3.3 :

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

Opcjonalny blok strzałki był nieobecny w Pythonie 2 i nie mogłem znaleźć żadnych informacji dotyczących jego znaczenia w Pythonie 3. Okazuje się, że jest to poprawny Python i jest akceptowany przez interpretera:

def f(x) -> 123:
    return x

Myślałem, że może to być jakaś składnia warunku wstępnego, ale:

  • Nie mogę xtutaj przetestować , ponieważ wciąż jest niezdefiniowany,
  • Bez względu na to, co wstawię po strzałce (np. 2 < 1), Nie wpływa to na zachowanie funkcji.

Czy ktoś przyzwyczajony do tej składni może to wyjaśnić?

Krotton
źródło

Odpowiedzi:

374

To adnotacja funkcji .

Bardziej szczegółowo, Python 2.x ma ciągi dokumentów, które umożliwiają dołączenie ciągu metadanych do różnych typów obiektów. Jest to niezwykle przydatne, dlatego Python 3 rozszerza tę funkcję, umożliwiając dołączanie metadanych do funkcji opisujących ich parametry i zwracane wartości.

Nie ma z góry założonego przypadku użycia, ale PEP sugeruje kilka. Jednym bardzo przydatnym jest umożliwienie opisywania parametrów ich oczekiwanymi typami; wówczas byłoby łatwo napisać dekorator, który weryfikuje adnotacje lub wymusza argumenty na odpowiedni typ. Innym jest zezwolenie na dokumentację specyficzną dla parametrów zamiast kodowania jej w dokumencie.

Katriel
źródło
122
Informacje są dostępne jako .__annotations__atrybut.
Martijn Pieters
8
Wow, przeoczyłem dość szeroki obszar wiedzy - nie tylko adnotacje o wartości zwracanej, ale także adnotacje dotyczące parametrów. Dziękuję Ci bardzo :).
Krotton,
4
@Krotton Nie mogę cię winić za to, że go przegapiłeś, jest praktycznie nieużywany. Zetknąłem się z nimi tylko z jedną biblioteką i jest to dość niejasne.
5
A __annotations__atrybutem jest słownik. Klucz returnsłuży do pobierania wartości za strzałką.
Keith,
9
@delnan - prawdopodobnie przyczyną tego, że jest w większości nieużywany, jest to, że większość bibliotek Pythona nadal stara się być kompatybilna z python2.x. W miarę, jak python3.x zaczyna być coraz bardziej standardowy, możemy zobaczyć więcej takich rzeczy pojawiających się tu i tam ...
mgilson 17.01.2013
251

Są to adnotacje funkcyjne zawarte w PEP 3107 . W szczególności ->oznacza adnotację funkcji powrotu.

Przykłady:

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

Adnotacje to słowniki, więc możesz to zrobić:

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

Możesz również mieć strukturę danych w języku Python, a nie tylko ciąg znaków:

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

Lub możesz użyć atrybutów funkcji do sprawdzenia poprawności wywoływanych wartości:

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)

Wydruki

>>> f(2,2) 
AssertionError: x==2; Test: _between: must be between 3 and 10
>>> f(3,2.1)
AssertionError: y==2.1; Test: <lambda>
św
źródło
86

Jak stwierdzono w innych odpowiedziach, ->symbol jest używany jako część adnotacji funkcji. W nowszych wersjach Pythona >= 3.5ma jednak określone znaczenie.

PEP 3107 - Adnotacje funkcji opisują specyfikację, definiując zmiany gramatyczne, istnienie, func.__annotations__w którym są one przechowywane, oraz fakt, że przypadek użycia jest nadal otwarty.

W Pythonie 3.5choć PEP 484 - Rodzaj wskazówki przywiązuje znaczenia do tego jednego: ->jest używany do wskazania typu, że funkcja zwraca. Wygląda również na to, że zostanie to wymuszone w przyszłych wersjach, jak opisano w Co z istniejącymi zastosowaniami adnotacji :

Najszybszy możliwy do wyobrażenia schemat wprowadziłby cichą deprecjację adnotacji nietypowych w 3.6, pełną deprecjację w 3.7 i zadeklarowałby podpowiedzi typu jako jedyne dozwolone użycie adnotacji w Pythonie 3.8.

(Moje podkreślenie)

O 3.6ile mi wiadomo, nie zostało to w rzeczywistości zaimplementowane, więc może zostać zderzone z przyszłymi wersjami.

Zgodnie z tym podany przykład:

def f(x) -> 123:
    return x

będzie zabronione w przyszłości (aw obecnych wersjach będzie mylące), należałoby go zmienić na:

def f(x) -> int:
    return x

aby skutecznie opisać tę funkcję fzwraca obiekt typu int.

Adnotacje nie są w żaden sposób wykorzystywane przez samego Pythona, raczej zapełniają je i ignorują. Współpracują z nimi biblioteki innych firm.

Dimitris Fasarakis Hilliard
źródło
64

W następującym kodzie:

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

po -> intprostu mówi, że f()zwraca liczbę całkowitą (ale nie zmusza funkcji do zwracania liczby całkowitej). Nazywa się to adnotacją zwrotną i można uzyskać do niej dostęp jako f.__annotations__['return'].

Python obsługuje również adnotacje parametrów:

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

: floatinformuje osoby, które czytają program (oraz niektóre biblioteki / programy innych firm, np. pylint), że xpowinien to być float. Jest dostępny jako f.__annotations__['x']i sam w sobie nie ma żadnego znaczenia. Więcej informacji znajduje się w dokumentacji:

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

MaxiMouse
źródło
4

Oznacza to rodzaj wyniku, który zwraca funkcja, ale może być None.

Jest szeroko rozpowszechniony we współczesnych bibliotekach zorientowanych na Python 3.x.

Na przykład w wielu miejscach w kodzie biblioteki znajduje się profilowanie pand bibliotek :

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
źródło
„Oznacza to typ wyniku, który zwraca funkcja, ale może to być Brak.” Może to być Brak lub dowolny inny typ.
Ebram Shehata
2

def function(arg)->123:

Jest to po prostu typ zwracany, liczba całkowita w tym przypadku nie ma znaczenia, jaką liczbę napiszesz.

jak Java :

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

Ale w przypadku Pythona (jak powiedział Jim Fasarakis Hilliard ) typ zwracany to tylko podpowiedź , więc sugeruje powrót, ale pozwala mimo to zwrócić inny typ jak ciąg znaków ..

Mike D3ViD Tyson
źródło
1
def f(x) -> 123:
    return x

Moje streszczenie:

  1. Po prostu ->wprowadzono, aby programiści mogli opcjonalnie określić typ zwracanej funkcji. Zobacz propozycję rozszerzenia Python 3107

  2. Jest to wskazówka, jak rzeczy mogą się rozwijać w przyszłości, gdy Python jest szeroko stosowany - wskazówka w kierunku silnego pisania - to moja osobista obserwacja.

  3. Możesz także określić typy argumentów. Określenie typu zwracanych funkcji i argumentów pomoże zmniejszyć błędy logiczne i ulepszyć ulepszenia kodu.

  4. Możesz mieć wyrażenia jako typ zwracany (zarówno na poziomie funkcji, jak i parametru), a wyniki wyrażeń można uzyskać za pomocą atrybutu „return” obiektu adnotacji . adnotacje będą puste dla wartości wyrażenia / zwrotu dla funkcji wstawianych lambda.

maz
źródło
Dziękuję za poprawki.
maz