[Python-Dev] What's missing in PEP-484 (Type hints)
Steven D'Aprano
steve at pearwood.info
Thu Apr 30 14:33:46 CEST 2015
On Thu, Apr 30, 2015 at 01:41:53PM +0200, Dima Tisnek wrote:
> # Syntactic sugar
> "Beautiful is better than ugly, " thus nice syntax is needed.
> Current syntax is very mechanical.
> Syntactic sugar is needed on top of current PEP.
I think the annotation syntax is beautiful. It reminds me of Pascal.
> # internal vs external
> @intify
> def foo() -> int:
> b = "42"
> return b # check 1
> x = foo() // 2 # check 2
>
> Does the return type apply to implementation (str) or decorated callable (int)?
I would expect that a static type checker would look at foo, and flag
this as an error. The annotation says that foo returns an int, but it
clearly returns a string. That's an obvious error.
Here is how I would write that:
# Perhaps typing should have a Function type?
def intify(func: Callable[[], str]) -> Callable[[], int]:
@functools.wraps(func)
def inner() -> int:
return int(func())
return inner
@intify
def foo() -> str:
b = "42"
return b
That should, I hope, pass the type check, and without lying about the
signature of *undecorated* foo.
The one problem with this is that naive readers will assume that
*decorated* foo also has a return type of str, and be confused. That's a
problem. One solution might be, "don't write decorators that change the
return type", but that seems horribly restrictive. Another solution
might be to write a comment:
@intify # changes return type to int
def foo() -> str:
...
but that's duplicating information already in the intify decorator, and
it relies on the programmer writing a comment, which people don't do
unless they really need to.
I think that the only solution is education: given a decorator, you
cannot assume that the annotations still apply unless you know what the
decorator does.
> How can same annotation or a pair of annotations be used to:
> * validate return statement type
> * validate subsequent use
> * look reasonable in the source code
>
>
> # lambda
> Not mentioned in the PEP, omitted for convenience or is there a rationale?
> f = lambda x: None if x is None else str(x ** 2)
> Current syntax seems to preclude annotation of `x` due to colon.
> Current syntax sort of allows lamba return type annotation, but it's
> easy to confuse with `f`.
I don't believe that you can annotate lambda functions with current
syntax. For many purposes, I do not think that is important: a good type
checker will often be able to infer the return type of the lambda, and
from that infer what argument types are permitted:
lambda arg: arg + 1
Obviously arg must be a Number, since it has to support addition with
ints.
> # local variables
> Not mentioned in the PEP
> Non-trivial code could really use these.
Normally local variables will have their type inferred from the
operations done to them:
s = arg[1:] # s has the same type as arg
When that is not satisfactory, you can annotate variables with a comment:
s = arg[1:] #type: List[int]
https://www.python.org/dev/peps/pep-0484/#id24
> # global variables
> Not mentioned in the PEP
> Module-level globals are part of API, annotation is welcome.
> What is the syntax?
As above.
> # comprehensions
> [3 * x.data for x in foo if "bar" in x.type]
> Arguable, perhaps annotation is only needed on `foo` here, but then
> how complex comprehensions, e.g. below, the intermediate comprehension
> could use an annotation
> [xx for y in [...] if ...]
A list comprehension is obviously of type List. If you need to give a
more specific hint:
result = [expr for x in things if cond(x)] #type: List[Whatever]
See also the discussion of "cast" in the PEP.
https://www.python.org/dev/peps/pep-0484/#id25
> # class attributes
> s = socket.socket(...)
> s.type, s.family, s.proto # int
> s.fileno # callable
> If annotations are only available for methods, it will lead to
> Java-style explicit getters and setters.
> Python language and data model prefers properties instead, thus
> annotations are needed on attributes.
class Thing:
a = 42 # can be inferred
b = [] # inferred as List[Any]
c = [] #type: List[float]
--
Steve
More information about the Python-Dev
mailing list