Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Examples of open-source projects using Python 3 function annotations

Tags:

python-3.x

Can anyone give me some examples of Python open-source projects using the function annotations introduced in Python 3?

I want to see some practical uses of this feature and see if I can use it my own project.

like image 871
gruszczy Avatar asked Mar 14 '11 14:03

gruszczy


People also ask

How do you annotate a function in Python?

To do function annotations, you annotate the arguments and the return value. 00:56 The syntax for arguments is the argument's name, colon ( : ), space, and then whatever the annotation is. And then the syntax for return types is that space, greater than symbol arrow ( -> ), and then the annotation.

Should you use Python function annotations?

Function annotations are completely optional both for parameters and return value. Function annotations provide a way of associating various parts of a function with arbitrary python expressions at compile time. The PEP-3107 makes no attempt to introduce any kind of standard semantics, even for the built-in types.

What are function annotations and how are they used in Python?

Function annotations, both for parameters and return values, are completely optional. Function annotations are nothing more than a way of associating arbitrary Python expressions with various parts of a function at compile-time. By itself, Python does not attach any particular meaning or significance to annotations.


2 Answers

I have never seen this feature used in the wild. However, one potential use of function annotations that I explored in an article on Python 3 that I wrote for USENIX ;Login: was for enforcing contracts. For example, you could do this:

from functools import wraps

def positive(x):
    'must be positive'
    return x > 0

def negative(x):
    'must be negative'
    return x < 0

def ensure(func):
    'Decorator that enforces contracts on function arguments (if present)'
    return_check = func.__annotations__.get('return',None)
    arg_checks = [(name,func.__annotations__.get(name))
                   for name in func.__code__.co_varnames]

    @wraps(func)
    def assert_call(*args):
        for (name,check),value in zip(arg_checks,args):
            if check: 
                assert check(value),"%s %s" % (name, check.__doc__)
        result = func(*args)
        if return_check:
            assert return_check(result),"return %s" % (return_check.__doc__)
        return result
    return assert_call

# Example use
@ensure
def foo(a:positive, b:negative) -> positive:
    return a-b

If you do this, you'll see behavior like this:

>>> foo(2,-3)
5
>>> foo(2,3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "ensure.py", line 22, in assert_call
    assert check(value),"%s %s" % (name, check.__doc__)
AssertionError: b must be negative

I should note that the above example needs to be fleshed out more to work properly with default arguments, keyword arguments, and other details. It's only a sketch of an idea.

Now, whether or not this is a good idea or not, I just don't know. I'm inclined to agree with Brandon that the lack of composability is a problem--especially if annotations start to be used by different libraries for different purposes. I also wonder if something like this contract idea couldn't be accomplished through decorators instead. For example, making a decorator that was used like this (implementation left as an exercise):

@ensure(a=positive,b=negative)
def foo(a,b):
    return a-b

A historial note, I've always kind of felt that function annotations were an outgrowth of discussions about "optional static typing" that the Python community had more than 10 years ago. Whether that was the original motivation or not, I just don't know.

like image 182
David Beazley Avatar answered Sep 28 '22 08:09

David Beazley


I will play the curmudgeon, and recommend against using the feature. Hopefully it will someday be removed. Python has so far done a great job of deploying features that are attractive, orthogonal, and that can be stacked — function decorators are a great example: if I use three different libraries that all want me to decorate my function, the result looks rather clean:

@lru_cache(max_items=5)
@require_basic_auth
@view('index.html')
def index(…):
    ⋮

But this newfangled awkward “annotations” feature takes Python in the opposite direction: because you can only annotate a given function exactly once, it breaks completely the ability to compose solutions out of various pieces. If you had two libraries that each wanted you to annotate the same function on their behalf, then, from what I can see, you would be completely stymied.

like image 20
Brandon Rhodes Avatar answered Sep 28 '22 07:09

Brandon Rhodes