Created
August 31, 2023 14:46
-
-
Save ncaq/bbc41e1d6b05c125515bf833293a5370 to your computer and use it in GitHub Desktop.
PythonでBがAを継承してもlist[B]変数をlist[A]を受け取る関数に渡せない理由の一つの具体例
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# Pythonのメジャーな型チェッカーは`B`が`A`を継承していても、 | |
# `list[A]`を受け取る関数に`list[B]`を渡すとエラーを出力します。 | |
# 型システム的な面倒な話は置いておいて、実際にこれがエラーじゃないと問題になってしまう例を示します。 | |
# 別にdataclassでなくても構わないのですが、printのやりやすさで使っています。 | |
from dataclasses import dataclass | |
# サブタイピング関係にあるクラスAとBを定義。 | |
@dataclass | |
class A: | |
x: int | |
@dataclass | |
class B(A): | |
y: int | |
# リストを定義。 | |
l0: list[B] = [B(0, 1)] | |
print(l0) # [B(x=0, y=1)] | |
# 実用的な例ではありませんが、リストの要素を全て固定値で埋めてしまう関数があるとします。 | |
def fill(xs: list[A]) -> None: | |
xs.clear() | |
xs += [A(0)] | |
# この関数にそのままの`list[A]`を呼び出しても型的には問題ありません。 | |
l1: list[A] = [A(1), A(2), A(3)] | |
fill(l1) | |
print(l1) # [A(x=0)] | |
# さて、`list[B]`を渡してみます。 | |
l2: list[B] = [B(1, 2), B(3, 4), B(5, 6)] | |
fill(l2) # Pyrightなどが全力で警告しますが無視します。 | |
print(l2) # [A(x=0)] | |
# さてl2要素にはB型のインスタンスが入っているはずなので、要素が存在していれば、以下は安全に実行できるはずです。 | |
print(l2[0].y) | |
# しかし、実行すると以下のようなエラーが出ます。 | |
# AttributeError: 'A' object has no attribute 'y' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment