# Proposal: Annotate types in multiple assignment

**URL:** <https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407>\
**Category:** Ideas\
**Created:** [May 15, 2024, 2:07pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407 "2024-05-15T14:07:57Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![huangyxi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/huangyxi/32/19895_2.png) [@huangyxi](https://discuss.python.org/u/huangyxi)\
**Post date:** [May 15, 2024, 2:07pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/1 "2024-05-15T14:07:57Z")

</div>

In the latest version of Python (3.12.3), type annotation for single variable assignment is available:

```python
a: int = 1

```

However, in some scenarios like when we want to annotate the tuple of variables in return, the syntax of type annotation is invalid:

```python
from typing import Any

def fun() -> Any: # when hard to annotate the strict type
    return 1, True

a: int, b: bool = fun() # INVALID

```

In this case, I propose two new syntaxes to support this feature:

1. Annotate directly after each variable:

```python
a: int, b: bool = 1, True
(a: int, b: bool) = 1, True

```

1. Annotate the tuple of return:

```python
(a, b): tuple[int, bool] = 1, True

```

## Appendix

In other programming languages, as I know, Julia and Rust support this feature in there approaches:

- Julia

```julia
a::Int, b::Bool = 1, true # VALID
(a::Int, b::Bool) = 1, true # VALID
(a, b)::Tuple{Int, Bool} = 1, true # INVALID

```

- Rust

```rust
let (a, b): (i64, bool) = (1, true); // VALID
let (a: i64, b: bool) = (1, true); // INVALID

```

---

<div class="post-metadata">

**Author:** ![Nineteendo](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nineteendo/32/19122_2.png) [@Nineteendo](https://discuss.python.org/u/Nineteendo)\
**Post date:** [May 15, 2024, 2:29pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/2 "2024-05-15T14:29:14Z")

</div>

I’m pretty sure this has already been suggested. Did you go through the mailing list and searched for topics here? Without doing that, there’s nothing to discuss here. (Besides linking to them).

---

<div class="post-metadata">

**Author:** ![Nineteendo](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nineteendo/32/19122_2.png) [@Nineteendo](https://discuss.python.org/u/Nineteendo)\
**Post date:** [May 15, 2024, 2:31pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/3 "2024-05-15T14:31:21Z")

</div>

Secondly, try to not edit posts, but post a followup. Some people read these topics in mailing list mode and don’t see your edits.

---

<div class="post-metadata">

**Author:** ![Nineteendo](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nineteendo/32/19122_2.png) [@Nineteendo](https://discuss.python.org/u/Nineteendo)\
**Post date:** [May 15, 2024, 2:39pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/4 "2024-05-15T14:39:03Z")

</div>

- [https://mail.python.org](https://mail.python.org)
- [https://mail.python.org/archives](https://mail.python.org/archives)

---

<div class="post-metadata">

**Author:** ![FelixLeg](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/felixleg/32/18439_2.png) [@FelixLeg](https://discuss.python.org/u/FelixLeg)\
**Post date:** [May 15, 2024, 2:46pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/5 "2024-05-15T14:46:07Z")

</div>

I, for one, welcome our ants overlords am all +1 for this. I also thought this was overlooked detail from Python’s developers 🙂

---

<div class="post-metadata">

**Author:** ![srittau](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/srittau/32/237_2.png) [@srittau](https://discuss.python.org/u/srittau)\
**Post date:** [May 15, 2024, 2:49pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/6 "2024-05-15T14:49:08Z")

</div>

For reference, PEP 526 has a note about this in the “Rejected/Postponed Proposals” section:

> **Allow type annotations for tuple unpacking:** This causes ambiguity: it’s not clear what this statement means:
> 
> `x, y: T`
> 
> Are `x` and `y` both of type `T`, or do we expect `T` to be a tuple type of two items that are distributed over `x` and `y`, or perhaps `x` has type `Any` and `y` has type `T`? (The latter is what this would mean if this occurred in a function signature.) Rather than leave the (human) reader guessing, we forbid this, at least for now.

Personally I think the meaning of this is rather clear, especially when combined with an assignment, and I would like to see this.

---

<div class="post-metadata">

**Author:** ![huangyxi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/huangyxi/32/19895_2.png) [@huangyxi](https://discuss.python.org/u/huangyxi)\
**Post date:** [May 15, 2024, 2:51pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/7 "2024-05-15T14:51:01Z")

</div>

Thank you for your valuable response, both regarding the discussion convention for Python development and the history of this feature.

I have found a related topic here:  
[https://mail.python.org/archives/list/python-ideas@python.org/thread/5NZNHBDWK6EP67HSK4VNDTZNIVUOXMRS/](https://mail.python.org/archives/list/python-ideas@python.org/thread/5NZNHBDWK6EP67HSK4VNDTZNIVUOXMRS/)

---

<div class="post-metadata">

**Author:** ![jamestwebber](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jamestwebber/32/12799_2.png) [@jamestwebber](https://discuss.python.org/u/jamestwebber)\
**Post date:** [May 15, 2024, 2:53pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/8 "2024-05-15T14:53:49Z")

</div>

Here’s the part I find unconvincing:

> [@huangyxi](#):
>
> ```python
> from typing import Any
> 
> def fun() -> Any: # when hard to annotate the strict type
> return 1, True
> 
> a: int, b: bool = fun() # INVALID
> 
> ```

Under what circumstances will `fun()` be hard to annotate, but `a, b` will be easy?

It’s better to annotate function arguments and return values, not variables. The preferred scenario is that `fun()` has a well-defined return type, and the type of `a, b` can be inferred (there is no reason to annotate it). This idea is presupposing there are cases where that’s difficult, but I’d like to see some examples where that applies.

---

<div class="post-metadata">

**Author:** ![Nineteendo](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nineteendo/32/19122_2.png) [@Nineteendo](https://discuss.python.org/u/Nineteendo)\
**Post date:** [May 15, 2024, 2:55pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/9 "2024-05-15T14:55:58Z")

</div>

Does this not work?

```python
from __future__ import annotations

def fun() -> tuple[int, bool]:
    return 1, True

```

---

<div class="post-metadata">

**Author:** ![jamestwebber](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jamestwebber/32/12799_2.png) [@jamestwebber](https://discuss.python.org/u/jamestwebber)\
**Post date:** [May 15, 2024, 2:56pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/10 "2024-05-15T14:56:33Z")

</div>

You don’t need `from __future__ ` as of… 3.9, I think?

---

<div class="post-metadata">

**Author:** ![FelixLeg](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/felixleg/32/18439_2.png) [@FelixLeg](https://discuss.python.org/u/FelixLeg)\
**Post date:** [May 15, 2024, 2:58pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/11 "2024-05-15T14:58:26Z")

</div>

Quick question that comes into my mind: If we can infer from a function declaration what is the type of variables on the other side of `=` then why there is the `a: bool = ...` allowed in the first place? 😕

---

<div class="post-metadata">

**Author:** ![Nineteendo](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nineteendo/32/19122_2.png) [@Nineteendo](https://discuss.python.org/u/Nineteendo)\
**Post date:** [May 15, 2024, 3:02pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/12 "2024-05-15T15:02:43Z")

</div>

3.10 if you want `A | B` too: [PEP 604](https://peps.python.org/pep-0604), although I’m not sure which version the OP is using and 3.9 hasn’t reached end of life yet.

---

<div class="post-metadata">

**Author:** ![jamestwebber](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jamestwebber/32/12799_2.png) [@jamestwebber](https://discuss.python.org/u/jamestwebber)\
**Post date:** [May 15, 2024, 3:03pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/13 "2024-05-15T15:03:38Z")

</div>

We can’t _always_ infer it, so annotating a variable is sometimes necessary or useful. But if the function’s return type is annotated then `a, b = fun()` allows type-checkers to infer the types of `a` and `b`. This stuff isn’t built in to Python and is evolving as typing changes, so what was inferred in the past might be better in the future.

So my question above was: are there any scenarios where annotating the function is difficult, but annotating the results would be easy? That seems like the motivating use case.

---

<div class="post-metadata">

**Author:** ![Nineteendo](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nineteendo/32/19122_2.png) [@Nineteendo](https://discuss.python.org/u/Nineteendo)\
**Post date:** [May 15, 2024, 3:09pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/14 "2024-05-15T15:09:53Z")

</div>

Would it be a solution to put it on the line above? And not allow assigning on the same line?  
Then it better mirrors function definitions.

```python
a: int, b: bool
a, b = fun()

```

It’s a long thread, so it might have been suggested already.

---

<div class="post-metadata">

**Author:** ![huangyxi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/huangyxi/32/19895_2.png) [@huangyxi](https://discuss.python.org/u/huangyxi)\
**Post date:** [May 15, 2024, 3:10pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/15 "2024-05-15T15:10:10Z")

</div>

Actually, in cases where the called function differs from the user-defined function, we should declare the types when assignment unpacking.

Here is a simplified MWE:

```python
import torch
from torch import nn as nn

class TestModel(nn.Module):
	def forward(
		self,
		x: torch.Tensor,
	) -> tuple[torch.Tensor, torch.Tensor]:
		a = 1 + x
		b = 2 * x
		return a, b

x = torch.tensor([1.0, 2.0, 3.0])
model = TestModel()
a, b = model(x) # VALID without annotation
a: torch.Tensor, b: torch.Tensor = model(x) # INVALID

```

NOTE: In PyTorch, the ` __call__ ` function is internally wrapped from `forward` .

---

<div class="post-metadata">

**Author:** ![Nineteendo](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nineteendo/32/19122_2.png) [@Nineteendo](https://discuss.python.org/u/Nineteendo)\
**Post date:** [May 15, 2024, 3:12pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/16 "2024-05-15T15:12:37Z")

</div>

Can’t you write this? That’s shorter than writing the type annotations.

```python
a, b = model.forward(x)

```

---

<div class="post-metadata">

**Author:** ![jamestwebber](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jamestwebber/32/12799_2.png) [@jamestwebber](https://discuss.python.org/u/jamestwebber)\
**Post date:** [May 15, 2024, 3:15pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/17 "2024-05-15T15:15:46Z")

</div>

This is the kind of example I was asking for, thanks. Is the problem that typing tools don’t trace the return type through the call because the wrapping isn’t in python?

---

<div class="post-metadata">

**Author:** ![Nineteendo](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nineteendo/32/19122_2.png) [@Nineteendo](https://discuss.python.org/u/Nineteendo)\
**Post date:** [May 15, 2024, 3:27pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/18 "2024-05-15T15:27:16Z")

</div>

I still suggest to read the thread you linked, like I’m doing right now.

---

<div class="post-metadata">

**Author:** ![huangyxi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/huangyxi/32/19895_2.png) [@huangyxi](https://discuss.python.org/u/huangyxi)\
**Post date:** [May 15, 2024, 3:28pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/19 "2024-05-15T15:28:15Z")

</div>

The ` __call__ ` function is not the same as `forward`. There might be many other preprocessing and postprocessing steps involved inside it.

> <https://github.com/pytorch/pytorch/blob/c53e0ac7ba7cba96793bd98065dbae6675b62c88/torch/nn/modules/module.py#L1654>

---

<div class="post-metadata">

**Author:** ![Nineteendo](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nineteendo/32/19122_2.png) [@Nineteendo](https://discuss.python.org/u/Nineteendo)\
**Post date:** [May 15, 2024, 3:31pm UTC](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407/20 "2024-05-15T15:31:36Z")

</div>

Yeah, quite a bit of pre-processing in fact… unless you don’t have hooks by the looks of it:

> <https://github.com/pytorch/pytorch/blob/c53e0ac7ba7cba96793bd98065dbae6675b62c88/torch/nn/modules/module.py#L1534-L1541>

[Next page](https://discuss.python.org/t/proposal-annotate-types-in-multiple-assignment/53407.md?page=2)
