Proposal: Annotate types in multiple assignment

Right, but it seems like PyTorch could have annotated all of these call functions using a ParamSpec and return value annotation thereby making __call__ have the same annotation as forward.

3 Likes
a: int|float = 12

# later...
a = 3.14

If you left the type checker to infer a’s type, it would choose int. In practice, the need for this is rare - I mostly see it when declaring instance variables that default to None: self.count: int|None = None

1 Like

That’s not always the case. Sometimes, you are fine with the type of the variable being inferred from the type of value assigned to them. Sometimes, you want an error to be raised if you know that, e.g., a should be an int and b should be a bool, but it turns out you use the wrong function to initialize them or the definition of the function has changed at some point.

OK, I went through the entire thread and I don’t think allowing the syntax outside of assignments has been brought up yet.

While this would be a syntax error:

a: int, b: bool = fun()

Why not just do one annotation per line? A personal opinion, but I think it’s better style.

There seems to be a lot of ideas over the last few years that revolve around save one line here or there while making code harder to read. I think this is a false economy.

Too be honest, in that case I just don’t write a type annotation. It’s just overkill (even for me).

I have a personal framework, where I often use the following:

tm_id: int
work_tag: str
tm_id, work_tag = self.path_args  # path_args is tuple[Any, ...]

# or even

tm_id: int
(tm_id, ) = self.path_args

I would personally prefer the following style, instead of spreading the variable definitions over multiple lines - and thereby putting more emphasis on them than they are worth:

tm_id: int, work_tag: str = self.path_args
# or
(tm_id: int,) = self.path_args
4 Likes

Personally, I would do this:

tm_id, work_tag = self.path_args
assert isinstance(tm_id, int)
assert isinstance(work_tag, str)

but I admit it depends on prevalence and context. It’s possible you may also be better doing match self.path_args.

One option that hasn’t been brought up yet is to do it on one line using cast.

When the return type depends on the arguments. Ideally function return type should be merged with explicit annotations. Like so:

from typing import Mapping
from matplotlib.axes import Axes
import matplotlib.pyplot as plt

fig, axes: Mapping[Any, Axes] = plt.subplots(2, 2)

subplots is annotated to return tuple[Figure, Any], so fig and axes should now be typed as Figure and Mapping respectively.

1 Like

You can already write

a: int; b: bool
a, b = fun()

I wouldn’t recommend it, like most usages of ;.

4 Likes

Isn’t it an array of axes? Anyway, polymorphic returns like this are usually poor interface design. Someone tried to save a comma and created headaches.

1 Like

If you call plt.subplots(1, 1, ....) then the result is tuple[Figure, Axes] (a single axes object), unless you also pass squeeze=False. I don’t think anyone would accuse matplotlib of having a great interface, but at this point it’s hard to change it.

Yes, I know how it works. It’s a bad interface. So I don’t think it’s a good justification for the proposed feature.

Agreed. I just thought you were asking about the type of the return value. :+1:

1 Like

Isn’t it an array of axes? Anyway, polymorphic returns like this are usually poor interface design. Someone tried to save a comma and created headaches.

Exactly. Therefore, I believe the proposed feature offers a simple and consistent solution to alleviate the headaches caused by code created by others, especially when it is difficult or time-consuming to fix their code.

Personally, I’d rather push people to write good interfaces, than make it easier to work around bad
ones. But I understand your point.

So, for this, I’d rather just do the annotations on a different line for these (somewhat rare) cases.

The feature already exists, what is discussed is a natural extension of it, and I personally would love to see it to be extended to for loops.

The best is the enemy of good. Python is a very flexible language used for prototyping and by people without CS degrees so I wouldn’t expect it to become a model of properly crafted and annotated interfaces any time soon, or ever.

2 Likes