[Tutor] Function annotations

Steven D'Aprano steve at pearwood.info
Sat Feb 4 22:23:55 EST 2017


On Sat, Feb 04, 2017 at 08:50:00PM -0600, boB Stepp wrote:
> I just finished looking at
> https://docs.python.org/3/tutorial/controlflow.html#function-annotations
> and skimming through PEP 484--Type Hints
> (https://www.python.org/dev/peps/pep-0484/).  My initial impression is
> that the purpose of function annotations is to enable static code
> analysis tools like linters to be more effective.

That's exactly what they're for.

> Aesthetically, to
> my eye it makes the function definition line more cluttered looking
> and more difficult to interpret at a glance.

Obviously they take up more room, but they also provide more 
information: the intended type of the argument,



> Of course, these are
> apparently optional.  I now wonder if I should be endeavoring to add
> these to my code?

Do you run a linter? If not, there doesn't seem much point in adding 
annotations.


> It is not clear to me how to implement function annotations in all
> instances.  Starting with a rather useless function:
> 
> def prt_stupid_msg():
>     print('Duh!')
> 
> This takes no arguments and returns None.  While this particular
> function is rather useless, many others of some utility may take no
> arguments and return None.  How should these types of functions be
> annotated?

Since it take no arguments, you cannot annotate the arguments it 
doesn't have.

You could annotate the return value:

def prt_stupid_msg() -> None: 
    print('Duh!')


but that's unlikely to be useful. I wouldn't bother.



> What about:
> 
> def print_stuff(stuff):
>     print(stuff)
> 
> Should this be annotated as:
> 
> def print_stuff(stuff: Any) -> None:
> 
> ?


Probably not. If you don't annotate the function, the linter should just 
assume it takes anything as argument.


> What about a function from the tutorial:
> 
> def make_incrementor(n):
>     return lambda x: x + n
> 
> n might always be an int or might always be a float or might be
> either, depending on the author's intent.  Therefore, how should n be
> annotated?  In accordance with the author's intentions?

Of course.

> And what
> about the case where n is sometimes an integer and sometimes a float?

That's a Union of two types. Think of Union as meaning "this OR that".


> And the return in this instance is a function.  Should the return be
> annotated "function"?

from typing import Union
from types import FunctionType

def make_incrementor(n: Union[int, float]) -> FunctionType:
    return lambda x: x + n



> And what about functions that return different types depending on
> conditional statements?

Probably a bad design...


> How would these varying return types be annotated?  Say:
> 
> def return_typed_value(desired_type, value):
>     if desired_type = 'string':
>         return str(value)
>     elif desired_type = 'integer':
>         return int(value)
>     elif desired_type = 'float':
>         return float(value)
> 
> What should I do with this?

Redesign it.

def return_typed_value(
        desired_type: str, value: Any) -> Union[str, int, float]:
    ...



> As written "desired_type" will always be
> a string, but "value" will be potentially several different types.

If you want to restrict the type of `value` depending on the value of 
`desired_type`, you cannot expect the compiler or linter or type-checker 
to do this at compile time. In general, it doesn't know what the value 
of `desired_type` is, so it can't tell whether the type of `value` is 
valid or not.

You would have to use a *dynamic* type check, rather than static:

def return_typed_value(desired_type, value):
    if desired_type == 'string':
        if isinstance(value, (int, float)):
            return str(value)
    elif desired_type == 'integer':
        if isinstance(value, (float, str)):
            return int(value)
    elif desired_type == 'float':
        if isinstance(value, (int, str)):
            return float(value)
    raise TypeError



When your type checking rules become this intricate, it's probably a bad 
idea.


-- 
Steve


More information about the Tutor mailing list