What are good uses for Python3's “Function Ann

2019-01-04 05:29发布

Function Annotations: PEP-3107

I ran across a snippet of code demonstrating Python3's function annotations. The concept is simple but I can't think of why these were implemented in Python3 or any good uses for them. Perhaps SO can enlighten me?

How it works:

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

Everything following the colon after an argument is an 'annotation', and the information following the -> is an annotation for the function's return value.

foo.func_annotations would return a dictionary:

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

What's the significance of having this available?

12条回答
甜甜的少女心
2楼-- · 2019-01-04 05:32

If you look at the list of benefits of Cython, a major one is the ability to tell the compiler which type a Python object is.

I can envision a future where Cython (or similar tools that compile some of your Python code) will use the annotation syntax to do their magic.

查看更多
叼着烟拽天下
3楼-- · 2019-01-04 05:33

Uri has already given a proper answer, so here's a less serious one: So you can make your docstrings shorter.

查看更多
兄弟一词,经得起流年.
4楼-- · 2019-01-04 05:35

Function annotations are what you make of them.

They can be used for documentation:

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

They can be used for pre-condition checking:

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

Also see http://www.python.org/dev/peps/pep-0362/ for a way to implement type checking.

查看更多
够拽才男人
5楼-- · 2019-01-04 05:36

It a long time since this was asked but the example snippet given in the question is (as stated there as well) from PEP 3107 and at the end of thas PEP example Use cases are also given which might answer the question from the PEPs point of view ;)

The following is quoted from PEP3107

Use Cases

In the course of discussing annotations, a number of use-cases have been raised. Some of these are presented here, grouped by what kind of information they convey. Also included are examples of existing products and packages that could make use of annotations.

  • Providing typing information
    • Type checking ([3], [4])
    • Let IDEs show what types a function expects and returns ([17])
    • Function overloading / generic functions ([22])
    • Foreign-language bridges ([18], [19])
    • Adaptation ([21], [20])
    • Predicate logic functions
    • Database query mapping
    • RPC parameter marshaling ([23])
  • Other information
    • Documentation for parameters and return values ([24])

See the PEP for more information on specific points (as well as their references)

查看更多
在下西门庆
6楼-- · 2019-01-04 05:37

The first time I saw annotations, I thought "great! Finally I can opt in to some type checking!" Of course, I hadn't noticed that annotations are not actually enforced.

So I decided to write a simple function decorator to enforce them:

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

I added it to the Ensure library.

查看更多
在下西门庆
7楼-- · 2019-01-04 05:38

This is a way late answer, but AFAICT, the best current use of function annotations is PEP-0484 and MyPy.

Mypy is an optional static type checker for Python. You can add type hints to your Python programs using the upcoming standard for type annotations introduced in Python 3.5 beta 1 (PEP 484), and use mypy to type check them statically.

Used like so:

from typing import Iterator

def fib(n: int) -> Iterator[int]:
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a + b
查看更多
登录 后发表回答