But isn't that the same way with type comments? Except uglier?
I think we're fine; there aren't any `AbstractAccountManagerInterfacePageSQLDatabaseConnPipeOrientedNewVersionProtocol`s.
> There is potential in this PEP, but in its current form I think it should be rejected.
>
> Cheers,
> Mark.
>
>
> _______________________________________________
> Python-Dev mailing list
> Pytho...@python.org
> https://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe: https://mail.python.org/mailman/options/python-dev/rymg19%40gmail.com
--
Ryan
[ERROR]: Your autotools build scripts are 200 lines longer than your program. Something’s wrong.
http://kirbyfan64.github.io/
It looks like you're misinterpreting the intent of the PEP. It does not mean legislate the behavior of the type checker in this way. In mypy, the first example is already rejected because it wants the annotation on the first occurrence. The plan is for mypy not to change its behavior -- the old form
TARGET = VALUE # type: TYPE
will be treated the same way as the new form
TARGET: TYPE = VALUE
(If you have a beef with what this means in mypy you should probably take it up with mypy, not with PEP 526.)
> It limits the use of variables
> ==============================
>
> In Python a name (variable) is just a binding that refers to an object.
> A name only exists in a meaningful sense once an object has been assigned to
> it. Any attempt to use that name, without an object bound to it, will result
> in a NameError.
(Or UnboundLocalError, if the compiler knows there is an assignment to the name anywhere in the same (function) scope.)
> PEP 526 makes variables more than just bindings, as any rebinding must
> conform to the given type. This looses us some of the dynamism for which we
> all love Python.
Thanks for catching this; that's not the intent.
> Quoting from the PEP:
> ```
> a: int
> a: str # Static type checker will warn about this.
> ```
> In other words, it is illegal for a checker to split up the variable, even
> though it is straightforward to do so.
One of my co-authors has gone too far here. The intent is not to legislate what should happen in this case but to leave it to the checker. In mypy, the equivalent syntax using type comments is currently indeed rejected, but we're considering a change here (https://github.com/python/mypy/issues/1174). The PEP 526 syntax will not make a difference here.
> However, without the type declarations,
> ```
> a = 1
> a = "Hi"
> ```
> is just fine. Useless, but fine.
And believe me, I want to keep it this way. I will amend the example and clarify the intent in the text.
> We should be free to add extra variables, whenever we choose, for clarity.
> For example,
> total = foo() - bar()
> should not be treated differently from:
> revenue = foo()
> tax = bar()
> total = revenue - tax
>
> If types are inferred, there is no problem.
> However, if they must be declared, then the use of meaningfully named
> variables is discouraged.
There is no mandate to declare variables! I actually see the main use of variable annotations in class bodies where it can crate a lot of clarity around which instance variables exist and what they mean.
> [A note about type-inference:
> Type inference is not a universal panacea, but it can make life a lot easier
> for programmers in statically type languages.
> Languages like C# use local type inference extensively and it means that
> many variables often do not need their type declared. We should take care
> not to limit the ability of checkers to infer values and types and make
> programmers' lives easier.
> Within a function, type inference is near perfect, failing only occasionally
> for some generic types.
> One place where type inference definitely breaks down is across calls, which
> is why PEP 484 is necessary.
> ]
Totally agreed. But type annotations are not *just* for the checker. I am regularly delighted when I find function annotations in code that I have to read for the first time, because it helps my understanding. Many others at Dropbox (where we have been doing a fairly large-scale experiment with the introduction of mypy) agree.
> It is premature
> ===============
>
> There are still plenty of issues to iron out w.r.t. PEP 484 types. I don't
> think we should be adding more syntax, until we have a *precise* idea of
> what is required.
>
> PEP 484 states:
> "If type hinting proves useful in general, a syntax for typing variables may
> be provided in a future Python version."
> Has it proved useful in general? I don't think it has. Maybe it will in
> future, but it hasn't yet.
PEP 526 does not alter this situation. It doesn't define new types, only new places where types can be used syntactically, and it is careful to give them the same syntactic description as PEP 484 (it's an expression). Practical use of mypy has shown that we use `# type` comments on variables with some regularity and not having them in the AST has been a problem for tools. For example, we have had to teach flake8 and pylint about type comments (so that we could continue to benefit from there "unused import" and "undefined variable" tests), and in both cases it was a gross hack.
> It seems confused about class attributes and instance attributes
> ================================================================
>
> The PEP also includes a section of how to define class attributes and
> instance attributes. It seems that everything needs to be defined in the
> class scope, even it is not an attribute of the class, but of its instances.
> This seems confusing, both to human reader and machine analyser.
And yet of course most other OO languages, like C++ and Java, also let you define instance and class variables in the class body (and again with a default of "instance", IIRC you have to use "static" in both languages for class variables).
> Example from PEP 526:
>
> class Starship:
>
> captain: str = 'Picard'
> damage: int
> stats: ClassVar[Dict[str, int]] = {}
>
> def __init__(self, damage: int, captain: str = None):
> self.damage = damage
> if captain:
> self.captain = captain # Else keep the default
>
> With type hints as they currently exist, the same code is shorter and
> doesn't contaminate the class namespace with the 'damage' attribute.
>
> class Starship:
>
> captain = 'Picard'
> stats = {} # type: Dict[str, int]
>
> def __init__(self, damage: int, captain: str = None):
> self.damage = damage # Can infer type as int
> if captain:
> self.captain = captain # Can infer type as str
It is also harder for the human reader to discover that there's a `damage` instance attribute. (Many classes I've reviewed at Dropbox have pages and pages of __init__ code, defining dozens of instance variables, sometimes using init-helper methods.)
For example, if you look at the code for asyncio's Future class you'll see this block at the class level:
```
class Future:
"""..."""
# Class variables serving as defaults for instance variables.
_state = _PENDING
_result = None
_exception = None
_loop = None
_source_traceback = None
_blocking = False # proper use of future (yield vs yield from)
_log_traceback = False # Used for Python 3.4 and later
_tb_logger = None # Used for Python 3.3 only
def __init__(self, *, loop=None):
...
```
The terminology here is actually somewhat confused, but these are all default values for instance variables. Because the defaults here are all immutable, the assignments are put here instead of in __init__ to save a little space in the dict and to make __init__ shorter (it only has to set those instance variables that have mutable values).
There's also a highly technical reason for preferring that some instance variables are given a default value in the class -- that way if an exception happens in __init__ and there is recovery code that tries to use some instance variable that __init__ hadn't initialized yet (e.g. in an attempt to log the object) it avoids AttributeErrors for those variables that have defaults set on the class. This has happened to me often enough that it is now a standard idiom in my head.
> This isn't an argument against adding type syntax for attributes in general,
> just that the form suggested in PEP 526 doesn't seem to follow Python
> semantics.
I'm happy to weaken the semantics as mandated in the PEP. In fact I had thought it already doesn't mandate any semantics (apart from storing certain forms of annotations in __annotations__, to match PEP 3107 and PEP 484), although I agree some examples have crept in that may appear more normative than we meant them.
> One could imagine applying minimal PEP 526 style hints, with standard Python
> semantics and relying on type inference, as follows:
>
> class Starship:
>
> captain = 'Picard'
> stats: Dict[str, int] = {}
>
> def __init__(self, damage: int, captain: str = None):
> self.damage = damage
> if captain:
> self.captain = captain
>
> The PEP overstates the existing use of static typing in Python
> ==============================================================
>
> Finally, in the rejected proposal section, under "Should we introduce
> variable annotations at all?" it states that "Variable annotations have
> already been around for almost two years in the form of type comments,
> sanctioned by PEP 484."
> I don't think that this is entirely true.
> PEP 484 was about the syntax for types, declaring parameter and return
> types, and declaring custom types to be generic.
> PEP 484 does include a description of type comments, but they are always
> annotations on assignment statements and were primarily intended for use in
> stub files.
That is a mis-characterization of the intent of type comments in PEP 484; they are not primarily meant for stubs (the only think I find tying the two together is the use of "..." as the initial value in stubs).
> Please don't turn Python into some sort of inferior Java.
> There is potential in this PEP, but in its current form I think it should be
> rejected.
Thanks for your feedback. We will be sure not to turn Python into Java! But I am unconvinced that your objections are reason to reject the PEP -- you seem to be fine with the general *syntax* proposed, your concerns are about the specific rules to be used by a type checker. I expect we'll be arguing about those for years to come -- maybe one day a PEP will come along that ties the semantics of types down, but PEP 526 is not it.
--
--Guido van Rossum (python.org/~guido)
The key difference is in placement.
PEP 484 style
variable = value # annotation
Which reads to me as if the annotation refers to the value.
PEP 526
variable: annotation = value
Which reads very much as if the annotation refers to the variable.
That is a change in terms of semantics and a change for the worse, in terms of expressibility.
It would be a real shame if PEP 526 mandates against checkers doing as good as job as possible. Forcing all uses of a variable to have the same type is a major and, IMO crippling, limitation.
E.g.
def foo(x:Optional[int])->int:
if x is None:
return -1
return x + 1
If the type of the *variable* 'x' is Optional[int] then 'return x + 1' doesn't type check. If the type of the *parameter* 'x' is Optional[int] then a checker can readily verify the above code.
class Reason(Enum):
timeout = 1
error = 2
def process(response: Union[str, Reason] = '') -> str:
if response is Reason.timeout:
return 'TIMEOUT'
elif response is Reason.error:
return 'ERROR'
else:
# response can be only str, all other possible values exhausted
return 'PROCESSED: ' + response> PEP 526
> variable: annotation = value
>
> Which reads very much as if the annotation refers to the variable.
Since the PEP makes it clear that the two forms are to be treated the
same, I think that whatever difference you think they have is not
relevant. They are *defined* to mean the same thing.
ClassVar)On 4 September 2016 at 21:32, Ivan Levkivskyi <levki...@gmail.com> wrote:
> The first form still could be interpreted by type checkers
> as annotation for value (a cast to more precise type):
>
> variable = cast(annotation, value) # visually also looks similar
I think a function based spelling needs to be discussed further, as it
seems to me that at least some of the goals of the PEP could be met
with a suitable definition of "cast" and "declare", with no syntactic
changes to Python. Specifically, consider:
def cast(value, annotation):
return value
def declare(annotation):
return object()
However, exploring this possibility still seems like a good idea to
me, as it should allow many of the currently thorny semantic questions
to be resolved, and a future syntax-only PEP for 3.7+ can just be
about defining syntactic sugar for semantics that can (by then)
already be expressed via appropriate initialisers.
So in this case, attempting to entirely defer specification of the
semantics creates a significant risk of type checkers written on the
assumption of C++ or Java style type declarations actively inhibiting
the dynamism of Python code, suggesting that the PEP would be well
advised to declare not only that the PEP 484 semantics are unchanged,
but also that a typechecker that flags the example above as unsafe is
wrong to do so.
I mostly agree, but the PEP still needs to address the fact that it's
only a subset of the benefits that actually require new syntax, since
it's that subset which provides the rationale for rejecting the use of
a function based approach, while the rest provided the incentive to
start looking for a way to replace the type comments.
I suspect you'll have an easier time of it on that front if you
include some examples of dynamically typed code that a well-behaved
type-checker *must* report as correct Python code, such as:
x: Optional[List[Any]]
# This is the type of "x" *after* the if statement, not *during* it
if arg is not None:
x = list(arg)
if other_arg is not None:
# A well-behaved typechecker should allow this due to
# the more specific initialisation in this particular branch
x.extend(other_arg)
else:
x = None
Similarly, it would be reasonable to say that these three snippets
should all be equivalent from a typechecking perspective:
x = None # type: Optional[T]
x: Optional[T] = None
x: Optional[T]
x = None
There are actually at least two separate cases: if x is a local
variable, the intention of `x: <type>` is quite different from when x
occurs in a class.
I am at a loss how to modify the PEP to avoid this misunderstanding,
since it appears it is entirely in the reader's mind. The PEP is not a
tutorial but a spec for the implementation, ...
Only for typecheckers, same as the plans for function level bare
annotations. Otherwise it wouldn't work, since you'd be calling
"all()" on a non-iterable :)
Guido doesn't like the syntax though, so the only place it would ever
appear is explanatory notes describing the purpose of the new syntax,
and hence can be replaced by something like:
# After all future assignments to x, check that x conforms to T
Cheers,
Nick.
P.S. Or, if you're particularly fond of mathematical notation, and we
take type categories as sets:
# ∀x: x ∈ T
That would be a singularly unhelpful explanatory comment for the vast
majority of folks, though :)