自動車学校に通い始めた六角レンチです
車体左側の感覚がなんもわからん
今回はpylanceで謎めいたエラーに遭遇したので紹介したいと思います
なお、Pythonのバージョンは3.11を使用しています
Union[str, None]をstr | Noneに短縮しているので注意
問題のコード
ある日、適当にプログラミングしてたら奇妙な型チェックエラーに遭遇する
a: list[str | None] | None = [None]*10
これはstrかNoneが含まれるリストか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]に似ていない。
さらに言えば、Noneがstr | Noneに似ていないと言っている。
でもどう考えてもNoneはstr | 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がどれに怒るのかというと...
なんとa = [None]*10とa = bにのみ怒る
他の代入方法もリスト的にはほぼ同じはずなのになぜかこれらにのみ怒る
さらにさらに深まる謎
もっと意味不明な点もある
a: list[str | None]
b = [None, None]
a = [None, None]
a = [None]*10
a = [None for _ in range(10)]
a = b
このコードはaの型ヒントでNoneの可能性を排除しただけなのだが...
今度は最後のa = bにのみ怒る
ちなみにリストの中身をNoneではなくstrのインスタンスにしても(例えば"a"だけにしても)発生する
なんでa = [None]*10に怒らなくなるのかは謎
一応この代入方法だと、リストの中身が全部同じものになるっていう仕様があるけどNoneはイミュータブルだから関係ないはず...
主に発生する場所
関数とかの引数で複数要素の値を許容する奴で起きる
こんな感じ
設計の意図的にはintかfloatが入ってたらいいですよって感じにしたかったのに、pylanceが許してくれない
解決策
エラー文をよく見ると...
Consider switching from "list" to "Sequence" which is covariant
と書いてある
listを使うな!Sequenceを使え!!!と言っている気がする。多分。
つまりこうすればよい
これなら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君はお布団にくるまっておねんねしてね
この添付コードと「お布団にくるまっておねんねしてね」は、何をしたいですか?
- その文字列をコードに組み込みたい?
- 型エラーの整理を続けたい?
- ほかの処理に使う定数化や関数化が必要?
う~ん、この。



