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 _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor