Что хорошо используется для "аннотаций функций Python3"



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



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



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



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


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

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



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


В чем смысл того, чтобы это было доступно?

593   12  

12 ответов:

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

Исходя из академического опыта, я могу сказать вам, что аннотации оказались неоценимыми для создания интеллектуальных статических анализаторов для таких языков, как 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.

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

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

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

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 также приводятся примеры использования, которые могут ответить на вопрос с точки зрения PEPs;)

Следующее цитируется из PEP3107

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

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

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

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

Как немного запоздалый ответ, несколько моих пакетов (marrow.скрипт, 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Comments

    Ничего не найдено.