typing.Union[A,B]からAに変換するには


結論を1行で言うと

typing.cast(A, Union[A,B]のオブジェクト)せよ。

発生した問題

コレクションオブジェクト(リストを保持するクラス)を実装しているときに遭遇した問題。

# 財布クラス(説明用に今考えた)
from typing import List, Union

class Wallet:
    def __init__(self, coins:  List[Coin]) -> None:
        self._wallet = coins

    def __getitem__(self, key: Union[int, slice]) -> Union[Coin, List[Coin]]:
        return self._wallet[key]

# 硬貨クラス
class Coin:
    def __init__(self, value: int) -> None:
        self.value = value

    @staticmethod
    def exchange(value) -> List[Coin]:
       # 複数の硬貨でお金を表現する。ロジックは割愛

上のようなクラスがあったとする。利用するとしたらこんな感じ。

coins = Coin.exchange(1000) # Coin(100)が10個のリストができるイメージ
wallet = Wallet(coins)

coin = wallet[0] # mypyに怒られる

何が起きているの?

最後の行ではmypyincompatible type "Union[List[Coin], Coin]"; expected "Coin"と怒られる。

__getitem__側はintsliceの両方に対応して、Union[Coin, List[Coin]]になっている。

それに対し、coin = wallet[0]の左辺ではCoinしか想定してないため、List[Coin]返ってきたらどうするの?責任取れるの?と聞いているわけ。

解決策

コーディングする側からすると、「察してくださいよ。まあ慌てるなよ」と言いたいところだが、そうしたことが通用する相手ではない。

from typing import cast
coin = cast(Coin, wallet[0])

と型のキャストをしてあげればよい。もちろん、値には変更はない。

参考