Какие хорошие применения для Python3 "Аннотации функций" - PullRequest
150 голосов
/ 14 июня 2010

Функциональные аннотации: 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}

Что значит иметь это в наличии?

Ответы [ 12 ]

86 голосов
/ 18 октября 2011

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

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

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

85 голосов
/ 14 июня 2010

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

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

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

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

38 голосов
/ 22 августа 2015

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

Mypy isнеобязательный статический тип проверки для 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
23 голосов
/ 03 октября 2010

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

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

20 голосов
/ 14 июня 2010

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

12 голосов
/ 16 февраля 2014

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

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

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 .

3 голосов
/ 28 октября 2014

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

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

Варианты использования

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

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

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

2 голосов
/ 13 мая 2015

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

Его META-данныечтобы объяснить, чтобы быть более явным о значениях функции.

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

ПРИМЕР:

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

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

https://github.com/ceronman/typeannotations

1 голос
/ 10 июня 2017

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

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

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

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

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

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

1 голос
/ 16 декабря 2014

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...