Allow annotations to be added wherever a variable may be declared


Allow annotations to be added wherever a variable may be declared, for example:

for i: str in xxxx:
    ...

if a: str := x.get("xx"):
    ...

This is equivalent to:

for i in xxxx:
    i: str

or

xxxx: list[str] = []
for i in xxxx:
    ...

Although this feature is not strictly necessary, I believe that annotations should be provided whenever a variable is declared.


The constructs you’re suggesting (loops like for x in container: and walrus like x := value) already include a colon (:). Even if you could write an unambiguous grammar for this, it would be extremely confusing.

Also, see Changing Python.

2 Likes

I don’t think this syntax should be encouraged for the walrus operator for readability concerns, but I can see its spirit being sometimes useful in compound statements.

Notably, context managers have as that are not idiomatically used outside their body, but technically remains bound after the context manager:

with open("text.txt", mode="r") as file:
    file: TextIO                # Explicit type annotation
    print(file.readline())

# "file" is technically still there, but is now useless to us.
file.read()       # Raises, file has been closed

# Can we safely reuse the name "file" for other things? The type checker won't allow us
with open("image.png", mode="rb") as file:    # Type check error
    ...

After the first context manager exits, file remains bound to a TextIO object and type checkers have inferred this correctly. Semantically speaking though, file is an “expired” object after the first context manager, and type checkers don’t yet know this.

Tying the annotation to the context manager helps:

  • denote that the object referred to by the name should not be ordinarily reused outside the context manager
  • allow type checkers to provide better warnings if we try to do so (and potentially highlight which methods can be called and which cannot)
  • allow possible reuse of that name without having to del it first (though this is not really encouraged)

Which is, the type checker would warn us on the file.read()outside the context manager, but would allow the second context manager to reuse the name.

If we can think of a more readable (and parseable) annotation syntax for context managers than colonss I think it’d be occasionally useful. But I don’t think we absolutely need this syntax to enable such tying though, it is enough for type checkers to recognize that a type declaration of the same name as the as of a containing context manager being tied to it.

There was just a long thread on why a context manager closing does not make a synbol invalid or expired:

https://discuss.python.org/t/scoped-context-manager-to-avoid-leaking-with-targets/

2 Likes

Did you know that you can just declare the type without a value?

i: str
for i in my_no_typed_list:
    print(i) #  expects string no matters what you put in my_no_typed_list
4 Likes

Strictly speaking, it would be

i: str
for i in xxxx:
    …

The annotation has to precede the assignment. Further, if iis already defined before the loop is reached, further annotation would not be allowed.

1 Like