Що означає -> у визначеннях функції Python?


476

Нещодавно я помітив щось цікаве, дивлячись на специфікацію граматики Python 3.3 :

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

Необов'язковий блок "стрілка" відсутній у Python 2, і я не зміг знайти жодної інформації щодо його значення в Python 3. Виявляється, це правильний Python і його прийнято інтерпретатором:

def f(x) -> 123:
    return x

Я думав, що це може бути якийсь синтаксис передумови, але:

  • Я не можу перевірити xтут, він ще не визначений,
  • Що б я не поставив після стрілки (наприклад 2 < 1), це не впливає на поведінку функції.

Чи може хтось, що звик із цим синтаксисом, пояснити це?

Відповіді:


375

Це анотація до функції .

Більш детально, Python 2.x має docstrings, які дозволяють приєднувати рядок метаданих до різних типів об'єкта. Це надзвичайно зручно, тому Python 3 розширює функцію, дозволяючи приєднувати метадані до функцій, що описують їх параметри та повернені значення.

Заздалегідь задуманого випадку використання немає, але PEP пропонує кілька. Один дуже зручний - дозволити коментувати параметри за очікуваними типами; Тоді було б легко написати декоратор, який перевіряє анотації або примушує аргументи до потрібного типу. Інше - дозволити документацію, що відповідає специфічним параметрам, а не кодувати її в docstring.


122
А інформація доступна як .__annotations__атрибут.
Martijn Pieters

8
Нічого собі, я пропустив досить широку область знань - не лише анотації з поверненим значенням, але й анотації параметрів. Дуже дякую :).
Кроттон

4
@Krotton Не можу звинувачувати вас у пропущенні, він практично не використовується. Я лише коли-небудь зустрічав одну бібліотеку, використовуючи їх, і це досить незрозуміло.

5
А __annotations__атрибут - словник. Ключ return- це той, який використовується для отримання значення після стрілки.
Кіт

9
@delnan - ймовірно, причина, що вона здебільшого не використовується, полягає в тому, що більшість бібліотек python досі прагнуть бути сумісними з python2.x. Коли python3.x починає ставати все більш стандартним, ми можемо побачити, що більше цих речей вискакує тут і там ...
mgilson

252

Це функціональні анотації, висвітлені в 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(20,3000),
      kinetic_energy.__annotations__['return'])
'90,000,000.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>

86

Як зазначено в інших відповідях, ->символ використовується як частина приміток до функцій. В останніх версіях Python >= 3.5він має певне значення.

PEP 3107 - Анотації щодо функцій описують специфікацію, визначаючи зміни граматики, існування, func.__annotations__в яких вони зберігаються, а також те, що справа її використання залишається відкритою.

У Python, 3.5однак, PEP 484 - Підказки типу додає цьому єдине значення: ->використовується для позначення типу, який функція повертає. Також здається, що це буде застосовано в наступних версіях, як описано в розділі Щодо існуючих застосувань приміток :

Найшвидше можлива схема запроваджуватиме мовчазне старіння анотацій підказки нетипового типу в 3.6, повна депресія в 3.7 та оголошення підказок типу як єдиного дозволеного використання анотацій у Python 3.8.

(Наголос мій)

3.6Наскільки я не можу сказати, це фактично не було реалізовано, щоб воно могло натрапити на майбутні версії.

Відповідно до цього прикладу, який ви надали:

def f(x) -> 123:
    return x

буде заборонено в майбутньому (а в поточних версіях буде заплутаним), його потрібно буде змінити на:

def f(x) -> int:
    return x

для цього ефективно описати, що функція fповертає об'єкт типу int.

Анотації жодним чином не використовується самим Python, він в значній мірі заповнює та ігнорує їх. Працювати з ними потрібно до сторонніх бібліотек.


64

У наступному коді:

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-definitions https://www.python.org/dev/peps/pep-3107/


4

Це означає тип результату, який функція повертає, але він може бути None .

Він широко поширений у сучасних бібліотеках, орієнтованих на Python 3.x.

Наприклад, він є в коді бібліотеки панд-профілювання в багатьох місцях, наприклад:

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." Це може бути Жоден або будь-який інший тип.
Ебрам Шегата

2

def function(arg)->123:

Це просто тип повернення, ціле число в цьому випадку не має значення, яке число ви пишете.

як Java :

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

Але для Python (як сказав Джим Фасаракіс Хілліард ) тип повернення - це лише натяк , тому він пропонує повернутись, але дозволити все одно повернути інший тип, як рядок ..


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

Мій підсумок:

  1. Просто ->вводиться для того, щоб розробники необов'язково вказали тип повернення функції. Див. Пропозицію щодо вдосконалення Python 3107

  2. Це вказівка ​​на те, як можуть розвиватися справи в майбутньому, коли Python прийнято широко - вказівка ​​на сильне друкування - це моє особисте спостереження.

  3. Ви також можете вказати типи для аргументів. Визначення типу повернення функцій та аргументів допоможе зменшити логічні помилки та покращити вдосконалення коду.

  4. Ви можете мати вирази як тип повернення (як на функціональному, так і на рівні параметрів), а до результатів виразів можна отримати доступ через атрибут об’єкта об’єкта анотацій . анотації будуть порожніми для значення вираження / повернення для вбудованих лямбда функцій.


Дякую за виправлення
маз
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.