0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

listの型ヒントには気をつけろ!!pylanceが急に殴ってくるぞ!!というかlist使うなSequence使え!!![Python]

Posted at

自動車学校に通い始めた六角レンチです
車体左側の感覚がなんもわからん

今回はpylanceで謎めいたエラーに遭遇したので紹介したいと思います

なお、Pythonのバージョンは3.11を使用しています
Union[str, None]str | Noneに短縮しているので注意

問題のコード

ある日、適当にプログラミングしてたら奇妙な型チェックエラーに遭遇する

a: list[str | None] | None = [None]*10

これはstrNoneが含まれるリストかNoneを受け取る変数にNoneのみのリストを代入しようとしているわけだが...

Type "list[None]" is not assignable to declared type "list[str | None] | None"
  Type "list[None]" is not assignable to type "list[str | None] | None"
    "list[None]" is not assignable to "list[str | None]"
      Type parameter "_T@list" is invariant, but "None" is not the same as "str | None"
      Consider switching from "list" to "Sequence" which is covariant
    "list[None]" is not assignable to "None"

なんかpylanceに怒られる

エラー文を見る感じ、どうやらlist[None]list[str | None]に似ていない。
さらに言えば、Nonestr | Noneに似ていないと言っている。

でもどう考えてもNonestr | Noneに対して内包関係であるはず

さらに深まる謎

さらに意味不明な点がある。
次のコードを見て欲しい

a: list[str | None] | None

b = [None, None]

a = [None, None]
a = [None]*10
a = [None for _ in range(10)]
a = b

ミューダブルだからa = bはまずいのではっていうのは一旦置いておいて、この4通りのaへの代入でpylanceがどれに怒るのかというと...

image.png

なんとa = [None]*10a = bにのみ怒る
他の代入方法もリスト的にはほぼ同じはずなのになぜかこれらにのみ怒る

さらにさらに深まる謎

もっと意味不明な点もある

a: list[str | None]

b = [None, None]

a = [None, None]
a = [None]*10
a = [None for _ in range(10)]
a = b

このコードはaの型ヒントでNoneの可能性を排除しただけなのだが...

image.png

今度は最後のa = bにのみ怒る

ちなみにリストの中身をNoneではなくstrのインスタンスにしても(例えば"a"だけにしても)発生する

なんでa = [None]*10に怒らなくなるのかは謎
一応この代入方法だと、リストの中身が全部同じものになるっていう仕様があるけどNoneはイミュータブルだから関係ないはず...

主に発生する場所

関数とかの引数で複数要素の値を許容する奴で起きる
こんな感じ

image.png

設計の意図的にはintfloatが入ってたらいいですよって感じにしたかったのに、pylanceが許してくれない

解決策

エラー文をよく見ると...

Consider switching from "list" to "Sequence" which is covariant

と書いてある

listを使うな!Sequenceを使え!!!と言っている気がする。多分。
つまりこうすればよい

image.png

これならpylanceも怒らないし、list以外のイテラブル(tupleとかsetとか)も許容するようになる

注意点はSequenceにしたらlist特有の操作(copyメソッドとか)が使えなくなることくらいかな?

おまけ

Copilotで新しく使えるようになったGPT-5が思ったより優秀なので、こいつにこの問題を聞こうと思います

原因
- aは「期待される型」がlist[str|None]。コンテキストからRHSのリストリテラルの要素型がOptional[str]に推論され、OK。
- bは「期待される型」がUnion[list[str|None], None]。Union文脈では多くの型チェッカーが要素型のコンテキスト推論を弱めるため、RHSはlist[None]と推論される。
- listは不変(invariant)。list[None]はlist[str|None]のサブタイプではないため、Unionのどの枝にも一致せずエラー。

対処例
- 右辺に明示的な型を与える(キャストや一時変数)。

````python
from typing import Optional, cast

a: list[Optional[str]] = [None] * 10

b: Optional[list[Optional[str]]]
b = cast(list[Optional[str]], [None] * 10)  # 明示キャスト
````

````python
from typing import Optional

tmp: list[Optional[str]] = [None] * 10
b: Optional[list[Optional[str]]] = tmp  # OK
````

````python
from typing import Optional

b: Optional[list[Optional[str]]] = None
b = [None for _ in range(10)]  # ツール次第でOK/NG。NGなら上記方法を推奨
````

ポイント
- 「listは不変」+「Union文脈ではコンテキスト推論が弱い」がエラーの理由。明示的に型を与えると解決します。

なんかよくわからんこと言ってる

list[None]list[str | None]のサブタイプではないとか言ってる
でもそれはa: list[str | None] = [None, None]でエラー出てないのに矛盾する

ということでGPT-5でもあんま有用な答えはもらえなかった
Copilot君はお布団にくるまっておねんねしてね

この添付コードと「お布団にくるまっておねんねしてね」は、何をしたいですか?
- その文字列をコードに組み込みたい?
- 型エラーの整理を続けたい?
- ほかの処理に使う定数化や関数化が必要?

う~ん、この。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?