It is interesting to contemplate how this could transform Python into nearly a statically typed language: x = 3 x = f(x)
If you say the type checker should infer that x is an int, and then therefore complain about x=f(x) if f() does not return an int, then we have what in new C++ is auto type declaration by default, and after that it's statically typed. So this would still work valStr = QueryMeasuringInstrument("app.Meas.P1.Result.Mean") val = float(valStr) but this would not: val = QueryMeasuringInstrument("app.Meas.P1.Result.Mean") val = float(val) even though it works today in both Python 2 and Python 3. I don't think the intent of var annotations is to automatically extract the type for variables that are not annotated, and then insist that they retain the same type at all times. It would break this example. I think if I do annotate a variable, THEN the type checker can insist that I do not change its type. So this could cause an error: string val = QueryMeasuringInstrument("app.Meas.P1.Result.Mean") val = float(val) --- Joe S. -----Original Message----- From: Steven D'Aprano <steve+comp.lang.pyt...@pearwood.info> Sent: Wednesday, July 4, 2018 11:31 AM To: python-list@python.org Subject: Re: PEP 526 - var annotations and the spirit of python On Wed, 04 Jul 2018 13:48:26 +0100, Bart wrote: > Presumably one type hint applies for the whole scope of the variable, > not just the one assignment. You know how in C you can write int x = 1; # the type applies for just this one assignment x = 2.5; # perfectly legal, right? Wait, no, of course you can't do that. Why would you suggest that as even a possibility? Of course the type (whether inferred or annotated) applies for the entire scope of that variable. > Which means that here: > > x: int = 3 > x = f(x) > > you know x should still an int after these two statements, because the > type hint says so. Without it: > > x = 3 > x = f(x) > > x could be anything. That's not how type checking works. It makes *no difference* whether the type is inferred or hinted. Type hints only exist to cover the cases the type inference engine can't determine, or determine too strictly. See below. In the Dark Ages of type-checking, the compiler was too dumb to work out for itself what the type of variables is, so you have to explicitly declare them all, even the most obvious ones. Given such a declaration: int x = 3; # using C syntax the type checker is smart enough to look at the next line: x = f(x); and complain with a type-error if f() returns (say) a string, or a list. Checking that the types are compatible is the whole point of type checking. Now fast forward to the Enlightenment of type-inference, first used in a programming language in 1973 (so older than half the programmers alive today). That purpose doesn't go away because we're using type inference. With type-inference, the type-checker is smart enough to recognise what type a variable is supposed to be (at least sometimes): x = 3; # of course it's an int, what else could it be? x = f(x); and likewise complain if f(x) returns something other than an int. There's no point in type checking if you don't, you know, actually *check* the types. With type inference, the only reason to declare a variable's type is if the type checker can't infer it from the code, or if it infers the wrong type. (More on this later.) To do otherwise is as pointless and annoying as those comments which merely repeat what the code does: import math # import the math module mylist.append(v) # append v to mylist counter += 1 # add 1 to counter s = s.upper() # convert s to uppercase x: int = 3 # assign the int 3 to x Don't be That Guy who writes comments stating the bleeding obvious. There's not always enough information for the type checker to infer the right type. Sometimes the information simply isn't there: x = [] # a list of what? and sometimes you actually did intend what looks like a type-error to the checker: x = 3 # okay, x is intended to be an int x = "spam" # wait, this can't be right In the later case, you can annotate the variable with the most general "any type at all" type: from typing import Any x: Any = 3 # x can be anything, but happens to be an int now x = "spam" # oh that's fine then or you can simply not check that module. (Type checking is optional, not mandatory.) >> A better example would be: >> >> x: int = None >> >> which ought to be read as "x is an int, or None, and it's currently >> None". > > In that case the type hint is lying. "Practicality beats purity." "This type, or None" is such a common pattern that any half-way decent type checker ought to be able to recognise it. You can, of course, explicitly annotate it: x: Optional[int] = None but the type checker should infer that if you assign None to a variable which is declared int, you must have meant Optional[int] rather than just int. If it doesn't, get a better type checker. Note that None is a special case (because sometimes special cases *are* special enough to break the rules). This should be an error: x: int = "hello world" # wait, that's not an int But this is fine: x: Union[int, str] = "hello world" x is permitted to be an int or a string, and it happens to currently be a string. > If both x and y have type hints of 'int', then with this: > > z = x + y > > you might infer that z will be also 'int' Indeed. If you inferred that z was a list, you're doing it wrong :-) > (whether it it type hinted or not). But if either of x and y can > be None, then this might not even execute. If x or y could be None, the type checker should complain that None does not support the + operator. The whole point of type checking is to find bugs like that at compile time. A type checker that doesn't, you know, *actually find type bugs* is a waste of time and effort. This is Type-Checking 101 stuff. A type checker which can't even recognise that None+1 is a type error is a pretty crappy type checker. Why do you assume that the state of the art in type-checkers in 2018 is *worse* than the state of the art in 1953 when Fortran came out? > If something is an int, then make it an int: > > x: int = 0 Indeed, that's often the best way, except for the redundant type hint, which makes you That Guy: x: int = 0 # set x to the int 0 But the point of my example was that x is not an int. It is an optional int, that is, an int or None. -- Steven D'Aprano "Ever since I learned about confirmation bias, I've been seeing it everywhere." -- Jon Ronson -- https://mail.python.org/mailman/listinfo/python-list