Каковы хорошие возможности использования «Аннотации функций» Python3,

Функция Аннотации: PEP-3107

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

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

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} 

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

12 Solutions collect form web for “Каковы хорошие возможности использования «Аннотации функций» Python3,”

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

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

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

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

Функциональные аннотации – это то, что вы делаете из них.

Они могут использоваться для документации:

 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/ для способа проверки типов.

Это способ позднего ответа, но 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 

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

 # 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.

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

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

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

 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 .

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

Ниже приводятся данные из PEP3107

Случаи использования

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

  • Предоставление информации о наборе текста
    • Проверка типа ([3], [4])
    • Пусть IDE показывают, какие типы ожидает функция и возвращает ([17])
    • Перегрузка функции / общие функции ([22])
    • Мосты на иностранных языках ([18], [19])
    • Адаптация ([21], [20])
    • Предикатные логические функции
    • Отображение запросов базы данных
    • Параметрирование параметра RPC ([23])
  • Дополнительная информация
    • Документация для параметров и возвращаемых значений ([24])

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

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

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

 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 

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

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

 def run(param1: int): """ Does things. :param param1: Needed for counting. """ pass 

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

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

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

Аннотации кодируются как :value после имени аргумента и перед значением по умолчанию, а as ->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 Pocket Reference, пятое издание

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

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

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

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

Цитата Что касается существующих применений аннотаций? :

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

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

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

  • Объяснение генератора чисел?
  • Как проверить, являются ли все элементы в списке целыми числами
  • Изменение каталога, в котором создаются файлы .pyc
  • обертывание текстового файла, чтобы каждая строка содержала не более 80 символов
  • Когда полезен объект ()?
  • Принуждение потока для блокировки всех остальных потоков от выполнения
  • Подматрица списка списков (без numpy)
  • Подавить / печатать без префикса b для байтов в Python 3
  • Python - лучший язык программирования в мире.