[Python-ideas] Proposal: Use mypy syntax for function annotations

Steven D'Aprano steve at pearwood.info
Thu Aug 14 20:15:54 CEST 2014


On Thu, Aug 14, 2014 at 12:01:37PM -0400, Sunjay Varma wrote:

> Though the annotation syntax is already present in Python 3, I would argue
> that using this for type annotations will get very messy very quickly. If
> I'm understanding the syntax correctly, writing any function using a large
> library with many nested subpackages could result in code like this:
> 
>     import twisted.protocols.mice.mouseman
> 
>     def
> process_mouseman(inputMouseMan: twisted.protocols.mice.mouseman.MouseMan)
> -> twisted.protocols.mice.mouseman.MouseMan:
>         pass

I would write that like this:

    from twisted.protocols.mice.mouseman import MouseMan
 
    def process_mouseman(inputMouseMan: MouseMan) -> MouseMan:
        pass


> That function definition is 122 characters long.

Or 58.


> It is also easy to see that it is very difficult to parse out what is going
> on in that function.

Only because I have no idea what MouseMan means :-)


> As an alternative, I would like to propose a syntax that Pycharm already
> supports:
> http://www.jetbrains.com/pycharm/webhelp/using-docstrings-to-specify-types.html
[...]
> Here's a taste of what that looks like:
>     class SimpleEquation(object):
> 
>         def demo(self, a, b, c):
>             """
>             This function returns the product of a, b and c
>             @type self: SimpleEquation
>             :param a: int - The first number
>             :param b: int
>             :param c: int - The third number should not be zero and should
> also
>                 only be -1 if you enjoy carrots (this comment spans 2 lines)
>             :return: int
>             """
>             return a * b * c

I really dislike that syntax. I dislike adding cruft like "@type" and 
":param" into docstrings, which should be written for human readers, not 
linters. I dislike that you have documented that self is a 
SimpleEquation. (What else could it be?) I dislike that the syntax 
clashes with ReST syntax. I dislike that it isn't obvious to me why the 
first parameter uses @type while the second parameter uses :param.


> Overall, I think overloading function declarations and inline comments is a
> bad idea. It promotes writing code with poor readability

I like the annotation syntax. I'm not completely convinced that the mypy 
syntax is mature enough to bless, but the basic idea of type annotations 
is pretty common in dozens of languages. I think you are in a tiny 
minority if you think that putting the type declaration right next to 
the parameter make it *less* clear that putting the type declaration in 
a completely different part of the code.

# the type is together with the parameter
def frobinate(x: Spam, y: Egg)->Breakfast:

# the type declaration and parameter are distantly apart
def frobinate(x, y): 
    """Return the frobinated x and y.

    Some more text goes here. Perhaps lots of text.

    :param x: Spam
    :param y: Eggs
    :return: Breakfast
    """


> On the original proposal:
> These changes really do seem to be overestimating the staticness of Python
> programs as well. What about functions that don't care about the type?

They can declare that they are object. Or not declare a type at all.

> What about functions that only want you to pass in an object that implements
> __iter__?


I would expect this should work:

from typing import Iter
def func(it:Iter):
    ...


> Python should not become a language where developers are required
> to add hundreds of odd cast() calls every time they choose to pass a
> different, but still compatible type, to a function.

I'm not sure how you go from *optional* static typing to developers 
being *required* to cast values.

As I see it, one HUGE advantage of this proposal is that people who want 
strict static typing currently might write code like this:

def make_sandwich(filling):
    if not isinstance(filling, Ham):
        raise TypeError
    ...


With the new proposal, they will probably write this:

def make_sandwich(filling: Ham):
    ...

and allow the static type check to occur at compile time. That means 
that if I want to pass a Spam instance instead of a Ham instance, all I 
need do is disable the compile-time type check, and make_sandwich will 
happily accept anything that has the same duck-type interface as Ham, 
like Spam. If I pass an int instead, I'll get the same run-time error 
that I would have got if make_sandwich did not include an explicit type 
check.

So, I think this proposal might actually lead to *more* duck typing 
rather than less, since you can always turn off the type checking.


-- 
Steven


More information about the Python-ideas mailing list