Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do function annotations need a dedicated syntax in Python?

Function annotations seem to duplicate behaviour already found in Python. Not only that, the meaning that they take on is not enforced in any way, so they may be used for any of the following documented in PEP 3107:

  • Providing typing information
  • Type checking
  • Let IDEs show what types a function expects and returns
  • Function overloading / generic functions
  • Foreign-language bridges
  • Adaptation
  • Predicate logic functions
  • Database query mapping
  • RPC parameter marshaling
  • Other information
  • Documentation for parameters and return values

or even something completely different.

In a way, function annotations reminds me of the old joke in Python's humour collection:

In fact, Python already supports block delimiters:

>
>     if foo: #{
>         foo1();
>         foo2();
>         foo3();
>     #}

in that

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

is not any more helpful than:

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

One may argue that function annotations are necessary because unlike comments, they can be accessed from within the code, like:

>>> def spam(a: 'eggs') -> 'ni!':
...     pass
...
>>> spam.__annotations__
{'a': 'eggs', 'return': 'ni!'}

although this same behaviour can easily be achieved with decorators, like:

def param(**kw):
    def decorator(func):
        def wrap(*args):
            print kw
            func(*args)
        return wrap
    return decorator

def return_(arg):
    def decorator(func):
        def wrap(*args):
            func(*args)
            print arg
        return wrap
    return decorator

@param(a='eggs')
@return_('ni!')
def spam(a):
    pass

spam(None)

# Output:
# -------
## {'a': 'eggs'}
## ni!

Python can already do what annotations do, so why do function annotations need a dedicated syntax?

EDIT: I am going to do a bit of expansion on my question, as its meaning has turned out to be slightly unclear.

I am asking this question specifically about function annotations as opposed to decorators, where

@decorator
def spam():
    pass

is short for

def spam():
    pass
spam = decorator(spam)

and method calling, where

self.method(param)

is short for

Class.method(self, param)

With these two shorthand syntactic shortcuts, their meanings cannot vary. I am not asking why such shortcuts are necessary when there are pre-existing alternatives; that is a matter of readability. Function annotations are slightly different than these two shortcuts, in that

def spam() -> int:
    pass

and

def spam() -> 'integer':
    pass

may have identical meanings to humans, but will not have identical meanings to a computer. Even if a programmer knows what the annotations should define, there is no agreed on definition of how the annotations define it. Furthermore, the annotations do not affect functionality, so there is no requirement to be consistent.

So here is my revised question:

Why do function annotations need a dedicated syntax when they provide a changeable and potentially inconsistent way of accessing already existing language features? Why is there no enforced definition on how to use annotations, when there is a perfect definition of what they can be used for (PEP 3107)?

like image 225
D K Avatar asked Aug 30 '12 18:08

D K


1 Answers

PEP 3107 that you link to seems to provide an answer to your question in its "rationale" section:

Rationale

Because Python's 2.x series lacks a standard way of annotating a function's parameters and return values, a variety of tools and libraries have appeared to fill this gap. Some utilise the decorators introduced in "PEP 318", while others parse a function's docstring, looking for annotations there.

This PEP aims to provide a single, standard way of specifying this information, reducing the confusion caused by the wide variation in mechanism and syntax that has existed until this point.

like image 164
retracile Avatar answered Nov 15 '22 03:11

retracile