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)?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With