Что означает -> в определениях функций Python?

477

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

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

Необязательный блок «стрелка» отсутствовал в Python 2, и я не смог найти никакой информации относительно его значения в Python 3. Оказалось, что это правильный Python, и он принят интерпретатором:

def f(x) -> 123:
    return x

Я думал, что это может быть какой-то синтаксис предварительного условия, но:

  • Я не могу проверить x здесь, это все еще не определено,
  • Независимо от того, что я поставил после стрелки (например 2 < 1), это не влияет на поведение функции.

Может ли кто-нибудь, привыкший к этому синтаксису, объяснить это?

Krotton
источник

Ответы:

376

Это функциональная аннотация .

Более подробно, в Python 2.x есть строки документации, которые позволяют вам присоединять строку метаданных к различным типам объектов. Это удивительно удобно, поэтому Python 3 расширяет эту функцию, позволяя вам присоединять метаданные к функциям, описывающим их параметры и возвращаемые значения.

Там нет предвзятого варианта использования, но ПКП предлагает несколько. Один очень удобный способ - позволить вам аннотировать параметры с их ожидаемыми типами; тогда было бы легко написать декоратор, который проверял бы аннотации или приводил аргументы к нужному типу. Другой - разрешить документацию по конкретным параметрам вместо того, чтобы кодировать ее в строку документации.

Katriel
источник
122
И информация доступна в качестве .__annotations__атрибута.
Мартин Питерс
8
Вау, я пропустил довольно обширную область знаний - не только аннотации возвращаемых значений, но и аннотации параметров. Большое спасибо :).
Кроттон
4
@ Кроттон Не могу винить вас за то, что вы его пропустили, он практически не используется. Я только когда-либо встречал одну библиотеку, используя их, и это довольно неясно.
5
А __annotations__атрибут это словарь. Ключ returnиспользуется для получения значения после стрелки.
Кит
9
@delnan - вероятно, причина в том, что он в основном не используется, потому что большинство библиотек python по-прежнему стремятся быть совместимыми с python2.x. По мере того, как python3.x начинает становиться все более стандартным, мы можем видеть, что больше и больше таких вещей появляются здесь и там ...
mgilson
253

Это функциональные аннотации, описанные в 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>
Dawg
источник
87

Как уже отмечалось в других ответах, ->символ используется как часть аннотаций функций. В более поздних версиях 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, он в значительной степени заполняет и игнорирует их. Работать с ними могут сторонние библиотеки.

Димитрис Фасаракис Хиллиард
источник
65

В следующем коде:

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/

MaxiMouse
источник
4

Это означает тип результата, который возвращает функция, но это может быть 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.
Vitalii
источник
«Это означает тип результата, который возвращает функция, но это может быть None». Это может быть None или любой другой тип.
Эбрам Шехата
2

def function(arg)->123:

Это просто тип возвращаемого значения, в данном случае целое число не имеет значения, какое число вы пишете.

как Java :

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

Но для Python (как сказал Джим Фасаракис Хиллиард ) возвращаемый тип - это просто подсказка , поэтому он предлагает возврат, но в любом случае разрешает возвращать другой тип, например, строку.

Майк Д3ВиД Тайсон
источник
1
def f(x) -> 123:
    return x

Мое резюме:

  1. Просто ->вводится, чтобы заставить разработчиков по желанию указать тип возвращаемого значения функции. См. Предложение по улучшению Python 3107

  2. Это признак того, как все может развиваться в будущем, так как Python получил широкое распространение - признак строгой типизации - это мое личное наблюдение.

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

  4. Вы можете иметь выражения как тип возвращаемого значения (для обоих на функции и уровне параметров) , а результат выражений можно получить с помощью аннотаций объекта атрибута «возврата». аннотации будут пустыми для выражения / возвращаемого значения для встроенных лямбда-функций.

маз
источник
Спасибо за исправления.
маз