Как правильно использовать аннотации функций Python3?

Аннотации функций: PEP-3107

Я наткнулся на фрагмент кода, демонстрирующий аннотации функций Python3. Идея проста, но я не могу понять, почему они были реализованы в Python3 или как-то их использовать. Может ТАК может меня просветить?

Как это работает:

def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
    ... function body ...

Все, что следует за двоеточием после аргумента, является «аннотацией», а информация, следующая за ->, является аннотацией для возвращаемого значения функции.

foo.func_annotations вернет словарь:

{'a': 'x',
 'b': 11,
 'c': list,
 'return': 9}

Какое значение имеет доступность этого?


person agscala    schedule 14.06.2010    source источник
comment
python.org/dev/peps/pep-3107/#use- кейсы   -  person SilentGhost    schedule 14.06.2010
comment
@SilentGhost: к сожалению, многие ссылки на реальные варианты использования не работают. Есть ли место, где контент мог храниться или он ушел навсегда?   -  person max    schedule 24.08.2012
comment
не должно foo.func_annotations быть foo.__annotations__ в python3?   -  person zhangxaochen    schedule 26.02.2014
comment
Аннотации не имеют особого значения. Единственное, что делает Python, - это помещает их в словарь аннотаций. Любое другое действие зависит от вас.   -  person N Randhawa    schedule 21.08.2016
comment
что значит def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):?   -  person Ali SH    schedule 03.08.2018


Ответы (12)


Я считаю, что это действительно здорово.

Исходя из академического опыта, я могу сказать вам, что аннотации оказались неоценимыми для включения интеллектуальных статических анализаторов для таких языков, как Java. Например, вы можете определить семантику, такую ​​как ограничения состояния, потоки, которым разрешен доступ, ограничения архитектуры и т. Д., И есть довольно много инструментов, которые затем могут читать их и обрабатывать их, чтобы обеспечить гарантии, выходящие за рамки того, что вы получаете от компиляторов. Вы даже можете написать вещи, которые проверяют предусловия / постусловия.

Я чувствую, что что-то подобное особенно необходимо в Python из-за более слабой типизации, но на самом деле не было конструкций, которые сделали бы это простым и частью официального синтаксиса.

Есть и другие варианты использования аннотаций, не зависящие от уверенности. Я вижу, как применить свои инструменты на основе Java к Python. Например, у меня есть инструмент, который позволяет вам назначать специальные предупреждения для методов и дает вам указание, когда вы вызываете их, что вы должны прочитать их документацию (например, представьте, что у вас есть метод, который нельзя вызывать с отрицательным значением, но он не интуитивно понятно из названия). С аннотациями я мог бы технически написать что-то подобное для Python. Точно так же инструмент, который организует методы в большом классе на основе тегов, может быть написан при наличии официального синтаксиса.

person Uri    schedule 14.06.2010
comment
ISTM - это теоретические преимущества, которые могут быть реализованы только в том случае, если стандартная библиотека и сторонние модули будут использовать аннотации функций и использовать их с последовательным смыслом и использовать хорошо продуманные системы аннотаций. До этого дня (который никогда не наступит) основными видами использования аннотаций функций Python будут одноразовые применения, описанные в других ответах. На время вы можете забыть об интеллектуальных статических анализаторах, гарантиях компилятора, цепочках инструментов на основе Java и т. Д. - person Raymond Hettinger; 22.01.2012
comment
Даже без использования аннотаций функций вы все равно можете использовать их для статического анализа в коде, который имеет их на входах и вызывает другой код с аналогичными аннотациями. В рамках более крупного проекта или кодовой базы это все еще может быть существенно полезным корпусом кода для выполнения статического анализа на основе аннотаций. - person gps; 01.04.2012
comment
AFAICT, вы можете сделать все это с помощью декораторов, которые появились раньше аннотаций; поэтому я до сих пор не вижу пользы. У меня немного другой взгляд на этот вопрос: stackoverflow.com/questions/13784713/ - person allyourcode; 09.12.2012
comment
Перенесемся в 2015 год, python.org/dev/peps/pep-0484 и mypy-lang.org начинают доказывать, что все скептики ошибаются. - person Mauricio Scheffer; 23.09.2015
comment
Это также еще больше раскрывает влияние Python на Swift. - person uchuugaka; 03.11.2015
comment
Приятно видеть, что даже такие великие люди, как @RaymondHettinger, время от времени ошибаются. (имеется в виду день, который никогда не наступит, но на самом деле пришел в форме PEP-484). Хотя, думаю, по хорошо продуманной части могут быть некоторые разногласия. : D - person Dustin Wyatt; 02.09.2018
comment
@DustinWyatt Я рад, что ошибся в этом прогнозе :-) Мы действительно получили стандартизированные типы из PEP 484 и в основном аннотированную стандартную библиотеку с typeshed. Однако список желаний OP для инструментов в стиле Java в основном еще не реализован. - person Raymond Hettinger; 04.09.2018

Аннотации функций - это то, что вы из них делаете.

Их можно использовать для документации:

def kinetic_energy(mass: 'in kilograms', velocity: 'in meters per second'):
     ...

Их можно использовать для проверки предварительных условий:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        msg = 'Var: {0}\tValue: {1}\tTest: {2.__name__}'.format(var, value, test)
        assert test(value), msg


def is_int(x):
    return isinstance(x, int)

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    return _between

def f(x: between(3, 10), y: is_int):
    validate(f, locals())
    print(x, y)


>>> f(0, 31.1)
Traceback (most recent call last):
   ... 
AssertionError: Var: y  Value: 31.1 Test: is_int

См. Также http://www.python.org/dev/peps/pep-0362/, чтобы узнать, как реализовать проверку типов.

person Raymond Hettinger    schedule 18.10.2011
comment
Чем это лучше, чем строка документации или явная проверка типа в функции? Кажется, что это усложняет язык без всякой причины. - person endolith; 24.04.2013
comment
@endolith Конечно, можно обойтись и без аннотаций функций. Они просто предоставляют стандартный способ доступа к аннотациям. Это делает их доступными для помощи () и подсказок, а также делает их доступными для самоанализа. - person Raymond Hettinger; 24.04.2013
comment
Вместо того, чтобы передавать числа, вы можете создать типы Mass и Velocity. - person rightfold; 19.07.2014
comment
чтобы полностью продемонстрировать это, я бы def kinetic_energy(mass: 'in kilograms', velocity: 'in meters per second') -> float: также показал тип возвращаемого значения. Это мой любимый ответ здесь. - person Tommy; 15.01.2016
comment
Есть ли способ проверить аннотацию return с помощью вашего кода? Кажется, он не появляется в locals - person user189728; 10.07.2018
comment
@ user189728 Вы правы. Либо возвращаемое значение необходимо сохранить в переменной, либо всю функцию нужно обернуть в проверяющий декоратор. - person Raymond Hettinger; 12.07.2018
comment
@RaymondHettinger Мне удалось сделать это с помощью декоратора, но это не очень красиво - person user189728; 12.07.2018

Это слишком поздний ответ, но, AFAICT, лучшее текущее использование аннотаций функций - PEP-0484 и MyPy.

Mypy - это необязательная программа проверки статического типа для Python. Вы можете добавлять подсказки типов в свои программы Python, используя будущий стандарт для аннотаций типов, представленный в Python 3.5 beta 1 (PEP 484), и использовать mypy для проверки типов статически.

Используется так:

from typing import Iterator

def fib(n: int) -> Iterator[int]:
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a + b
person Dustin Wyatt    schedule 22.08.2015
comment
Дополнительные примеры см. Здесь, Mypy Примеры и здесь Как извлечь выгоду из подсказок типа - person El Ruso; 06.11.2015
comment
Также см. pytype - другой статический анализатор, созданный с учетом PEP-0484. - person gps; 12.07.2016
comment
К сожалению, тип не применяется. Если я наберу list(fib('a')) с вашей примерной функцией, Python 3.7 с радостью примет аргумент и пожалуется, что нет возможности сравнить строку и int. - person Denis de Bernardy; 13.06.2019
comment
@DenisdeBernardy Как объясняет PEP-484, Python предоставляет только аннотации типов. Чтобы обеспечить соблюдение типов, вы должны использовать mypy. - person Dustin Wyatt; 15.06.2019

Просто чтобы добавить конкретный пример хорошего использования из моего ответа здесь в сочетании с декораторами может быть реализован простой механизм для мультиметодов.

# This is in the 'mm' module

registry = {}
import inspect

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function

def multimethod(function):
    name = function.__name__
    mm = registry.get(name)
    if mm is None:
        mm = registry[name] = MultiMethod(name)
    spec = inspect.getfullargspec(function)
    types = tuple(spec.annotations[x] for x in spec.args)
    mm.register(types, function)
    return mm

и пример использования:

from mm import multimethod

@multimethod
def foo(a: int):
    return "an int"

@multimethod
def foo(a: int, b: str):
    return "an int and a string"

if __name__ == '__main__':
    print("foo(1,'a') = {}".format(foo(1,'a')))
    print("foo(7) = {}".format(foo(7)))

Это можно сделать, добавив типы в декоратор как исходное сообщение Гуидо показывает, но аннотирование самих параметров лучше, так как это позволяет избежать возможности неправильного сопоставления параметров и типов.

Примечание. В Python вы можете обращаться к аннотациям как function.__annotations__, а не как function.func_annotations, поскольку стиль func_* был удален в Python 3.

person Muhammad Alkarouri    schedule 03.10.2010
comment
Интересное приложение, хотя, боюсь, function = self.typemap.get(types) не будет работать, когда задействованы подклассы. В этом случае вам, вероятно, придется пройти через typemap, используя isinnstance. Интересно, правильно ли справляется с этим @overload - person Tobias Kienzler; 28.08.2013
comment
Я думаю, что это не работает, если функция имеет возвращаемый тип - person zenna; 16.10.2016
comment
__annotations__ - это dict, который не обеспечивает порядок аргументов, поэтому этот фрагмент иногда терпит неудачу. Я бы порекомендовал изменить types = tuple(...) на spec = inspect.getfullargspec(function), затем types = tuple([spec.annotations[x] for x in spec.args]). - person xoolive; 15.12.2016
comment
Вы совершенно правы, @xoolive. Почему бы вам не отредактировать ответ, чтобы добавить исправление? - person Muhammad Alkarouri; 15.12.2016
comment
@xoolive: заметил. Иногда редакторы используют тяжелую руку для управления правками. Я отредактировал вопрос, чтобы включить ваше исправление. На самом деле, у меня было обсуждение этого вопроса, но там невозможно отменить исправление. Кстати, спасибо за помощь. - person Muhammad Alkarouri; 20.12.2016

Ури уже дал правильный ответ, так что вот менее серьезный: чтобы вы могли сократить свои строки документации.

person JAB    schedule 14.06.2010
comment
любить это. +1. однако, в конце концов, написание строк документации по-прежнему остается способом номер один для того, чтобы сделать мой код читаемым, однако, если бы вы реализовали какой-либо вид статической или динамической проверки, было бы неплохо иметь это. Возможно, я найду ему применение. - person Warren P; 10.07.2010
comment
Я не рекомендую использовать аннотации в качестве замены Args: section или строк @param или аналогичных в ваших строках документации (какой бы формат вы ни выбрали). Хотя аннотации документации служат прекрасным примером, они бросают тень на потенциальную силу аннотаций, поскольку могут помешать другим более мощным применениям. Кроме того, вы не можете опускать аннотации во время выполнения, чтобы уменьшить потребление памяти (python -OO), как это можно сделать со строками документации и утверждениями. - person gps; 01.04.2012
comment
@gps: Как я уже сказал, это был менее серьезный ответ. - person JAB; 30.04.2012
comment
Если серьезно, это гораздо лучший способ документировать типы, которые вы ожидаете, при этом придерживаясь DuckTyping. - person Marc; 24.10.2015
comment
@gps Я не уверен, что потребление памяти строками документации вызывает беспокойство в 99,999% случаев. - person Tommy; 15.01.2016
comment
Это не так. Хотя у меня случай 0,001%. ;) - person gps; 12.07.2016

Когда я впервые увидел аннотации, я подумал: «Отлично! Наконец-то я могу принять участие в проверке типов!» Конечно, я не заметил, что аннотации на самом деле не применяются.

Поэтому я решил написать простой декоратор функций для их применения:

def ensure_annotations(f):
    from functools import wraps
    from inspect import getcallargs
    @wraps(f)
    def wrapper(*args, **kwargs):
        for arg, val in getcallargs(f, *args, **kwargs).items():
            if arg in f.__annotations__:
                templ = f.__annotations__[arg]
                msg = "Argument {arg} to {f} does not match annotation type {t}"
                Check(val).is_a(templ).or_raise(EnsureError, msg.format(arg=arg, f=f, t=templ))
        return_val = f(*args, **kwargs)
        if 'return' in f.__annotations__:
            templ = f.__annotations__['return']
            msg = "Return value of {f} does not match annotation type {t}"
            Check(return_val).is_a(templ).or_raise(EnsureError, msg.format(f=f, t=templ))
        return return_val
    return wrapper

@ensure_annotations
def f(x: int, y: float) -> float:
    return x+y

print(f(1, y=2.2))

>>> 3.2

print(f(1, y=2))

>>> ensure.EnsureError: Argument y to <function f at 0x109b7c710> does not match annotation type <class 'float'>

Я добавил его в библиотеку Ensure.

person weaver    schedule 16.02.2014
comment
У меня такое же разочарование после того, как я был уверен, что в Python наконец-то есть проверка типов. Наконец-то придется перейти к реализации самодельной проверки типов. - person Hibou57; 19.07.2014

Прошло много времени с тех пор, как об этом спрашивали, но приведенный в вопросе фрагмент примера (как указано там) из PEP 3107, а в конце этого примера PEP также приведены варианты использования, которые могут ответить на вопрос из точки PEP Посмотреть ;)

Ниже приводится цитата из PEP3107.

Примеры использования

В ходе обсуждения аннотаций был поднят ряд вариантов использования. Некоторые из них представлены здесь, сгруппированные по типу информации, которую они передают. Также включены примеры существующих продуктов и пакетов, в которых можно использовать аннотации.

  • Providing typing information
    • Type checking ([3], [4])
    • Пусть IDE показывают, какие типы ожидает и возвращает функция ([17])
    • Перегрузка функций / общие функции ([22])
    • Иноязычные мосты ([18], [19])
    • Адаптация ([21], [20])
    • Функции логики предикатов
    • Отображение запросов к базе данных
    • Маршалинг параметров RPC ([23])
  • Other information
    • Documentation for parameters and return values ([24])

См. PEP для получения дополнительной информации по конкретным вопросам (а также их ссылки).

person klaas    schedule 28.10.2014
comment
Я был бы очень признателен, если бы те, кто проголосовал против, оставили хотя бы короткий комментарий, что вызвало голосование против. Это действительно помогло бы (по крайней мере мне) стать лучше. - person klaas; 24.07.2017

Python 3.X (только) также обобщает определение функции, позволяя аннотировать аргументы и возвращаемые значения значениями объектов для использования в расширениях.

Его МЕТА-данные для объяснения, чтобы быть более точными в отношении значений функции.

Аннотации кодируются как :value после имени аргумента и перед значением по умолчанию и как ->value после списка аргументов.

Они собираются в атрибут __annotations__ функции, но в противном случае сам Python не рассматривает их как особые:

>>> def f(a:99, b:'spam'=None) -> float:
... print(a, b)
...
>>> f(88)
88 None
>>> f.__annotations__
{'a': 99, 'b': 'spam', 'return': <class 'float'>}

Источник: карманный справочник Python, пятое издание.

ПРИМЕР:

Модуль typeannotations предоставляет набор инструментов для проверки типов и вывода типов кода Python. Он также предоставляет набор типов, полезных для аннотирования функций и объектов.

Эти инструменты в основном предназначены для использования в статических анализаторах, таких как линтеры, библиотеки автозавершения кода и IDE. Кроме того, предоставляются декораторы для выполнения проверок во время выполнения. Проверка типов во время выполнения не всегда является хорошей идеей в Python, но в некоторых случаях может быть очень полезной.

https://github.com/ceronman/typeannotations

Как набор текста помогает писать лучший код

Ввод текста может помочь вам выполнить статический анализ кода, чтобы выявить типовые ошибки перед отправкой кода в производство и предотвратить некоторые очевидные ошибки. Существуют такие инструменты, как mypy, которые вы можете добавить в свой набор инструментов в рамках жизненного цикла программного обеспечения. mypy может проверять правильность типов, частично или полностью работая с вашей кодовой базой. mypy также помогает обнаруживать ошибки, такие как проверка типа None, когда значение возвращается из функции. Ввод текста помогает сделать ваш код чище. Вместо документирования кода с помощью комментариев, где вы указываете типы в строке документации, вы можете использовать типы без каких-либо затрат на производительность.

Чистый Python: элегантное кодирование на Python ISBN: ISBN-13 (pbk): 978-1-4842-4877-5

PEP 526 - синтаксис для аннотаций переменных

https://www.python.org/dev/peps/pep-0526/ < / а>

https://www.attrs.org/en/stable/types.html

person The Demz    schedule 12.05.2015
comment
@BlackJack, было непонятно для использования в расширениях? - person The Demz; 05.12.2017
comment
Понятно, но ИМХО не отвечает на вопрос. Это все равно, что ответить «Как лучше всего использовать классы?» На «Для использования в программах». Это ясно, правильно, но спрашивающая сторона не совсем понимает, какое чертовски хорошее конкретное использование. Ваш ответ не может быть более общим, с примером, который по сути совпадает с тем, который уже был в вопросе. - person BlackJack; 05.12.2017

Несмотря на все описанные здесь варианты использования, одно обязательное и, скорее всего, принудительное использование аннотаций будет для введите подсказки.

В настоящее время это никоим образом не применяется, но, судя по PEP 484, будущие версии Python будут разрешать только типы в качестве значения для аннотаций.

Цитата Как насчет существующего использования аннотаций ?:

Мы действительно надеемся, что подсказки типов в конечном итоге станут единственным средством использования аннотаций, но это потребует дополнительного обсуждения и периода прекращения поддержки после первоначального развертывания модуля ввода с Python 3.5. Текущий PEP будет иметь предварительный статус (см. PEP 411) до тех пор, пока не будет выпущен Python 3.6. Самая быстрая из мыслимых схем будет вводить молчаливое прекращение поддержки аннотаций без подсказок типа в 3.6, полное исключение в 3.7 и объявление подсказок типов как единственное разрешенное использование аннотаций в Python 3.8.

Хотя я еще не видел никаких скрытых устаревших версий в 3.6, вместо этого вполне можно было бы повысить его до 3.7.

Таким образом, даже если могут быть и другие хорошие варианты использования, лучше всего оставить их исключительно для подсказок типов, если вы не хотите постоянно менять все в будущем, когда это ограничение будет действовать.

person Dimitris Fasarakis Hilliard    schedule 10.06.2017

В качестве немного отложенного ответа некоторые из моих пакетов (marrow.script, WebCore и т. Д.) Также используют аннотации, где они доступны, для объявления преобразования типов (т. Е. Преобразования входящих значений из Интернета, определения того, какие аргументы являются логическими переключателями и т. Д.). как выполнить дополнительную разметку аргументов.

Сценарий Marrow создает полный интерфейс командной строки для произвольных функций и классов и позволяет определять документацию, приведение и значения по умолчанию, полученные от обратного вызова, с помощью аннотаций с декоратором для поддержки более старых сред выполнения. Все мои библиотеки, использующие аннотации, поддерживают формы:

any_string  # documentation
any_callable  # typecast / callback, not called if defaulting
(any_callable, any_string)  # combination
AnnotationClass()  # package-specific rich annotation object
[AnnotationClass(), AnnotationClass(), …]  # cooperative annotation

«Чистая» поддержка строк документации или функций приведения типов упрощает смешивание с другими библиотеками, поддерживающими аннотации. (То есть есть веб-контроллер, использующий приведение типов, которое также может быть представлено в виде сценария командной строки.)

Отредактировано для добавления: Я также начал использовать TypeGuard пакет с использованием утверждений времени разработки для проверки. Преимущество: при запуске с включенной "оптимизацией" (-O / PYTHONOPTIMIZE env var) проверки, которые могут быть дорогостоящими (например, рекурсивными), опускаются, с мыслью о том, что вы правильно протестировали свое приложение в процессе разработки, поэтому проверки не нужны в производство.

person amcgregor    schedule 16.12.2014

Аннотации можно использовать для простой модуляции кода. Например. модуль для программы, которую я поддерживаю, может просто определять такой метод, как:

def run(param1: int):
    """
    Does things.

    :param param1: Needed for counting.
    """
    pass

и мы могли бы попросить пользователя указать элемент с именем "param1", который является "Необходимым для подсчета" и должен быть "int". В конце концов, мы даже можем преобразовать строку, предоставленную пользователем, в желаемый тип, чтобы получить максимально простой опыт.

См. объект метаданных нашей функции для открытого исходного кода. класс, который помогает в этом и может автоматически извлекать необходимые значения и преобразовывать их в любой желаемый тип (поскольку аннотация является методом преобразования). Даже IDE правильно показывают автозаполнение и предполагают, что типы соответствуют аннотациям - идеально подходит.

person Lasse Schuirmann    schedule 02.05.2015

Если вы посмотрите на список преимуществ Cython, основным из них является возможность указать компилятору, к какому типу относится объект Python.

Я могу представить себе будущее, в котором Cython (или аналогичные инструменты, которые компилируют часть вашего кода Python) будут использовать синтаксис аннотаций для выполнения своих задач.

person boardrider    schedule 30.07.2016
comment
RPython Annotator - это пример подхода, который кажется подходящим для Pythonic; после создания графика вашего приложения он может определить тип каждой переменной и (для RPython) обеспечить безопасность одного типа. OTOH требует бокса или других решений / обходных путей, чтобы учесть динамические богатые значения. Кто я такой, чтобы заставить мою multiply функцию работать только с целыми числами, когда 'na' * 8 + ' batman!' полностью действительна? ;) - person amcgregor; 19.01.2019