Что хорошего для Python3 "Annotations Function"

Функция Аннотации: 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}

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

Ответ 1

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

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

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

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

Ответ 5

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

Ответ 6

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

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

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.

Ответ 7

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

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

Использовать случаи

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

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

Подробнее о конкретных точках (а также их ссылках) см. PEP

Ответ 8

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

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

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

Clean Python: Elegant Coding in 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

Ответ 9

В качестве отложенного ответа некоторые из моих пакетов (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) проверки, которые могут быть дорогостоящими (например, рекурсивными), опускаются, -O PYTHONOPTIMIZE что вы должным образом протестировали свое приложение в процессе разработки, поэтому проверки следует ненужный в производстве.

Ответ 10

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

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

Цитата Как насчет существующих аннотаций?:

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

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

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

Ответ 11

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

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

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

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

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

Ответ 12

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

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