Python の Self 型と再帰的な型定義

Self 型は Python 3.11 で導入され、メソッドが自身のクラス型を返すことを正確に表現できます。継承時にも正しい型が推論されます。

問題の背景

従来、自身を返すメソッドの型付けは面倒でした。

from typing import TypeVar

T = TypeVar("T", bound="Builder")

class Builder:
    def set_name(self: T, name: str) -> T:
        self.name = name
        return self

クラスごとに TypeVar を定義する必要がありました。

Self 型の基本

from typing import Self

class Builder:
    def set_name(self, name: str) -> Self:
        self.name = name
        return self
    
    def set_value(self, value: int) -> Self:
        self.value = value
        return self

Self は「このメソッドを呼び出したインスタンスの型」を意味します。

継承での動作

from typing import Self

class Animal:
    def set_name(self, name: str) -> Self:
        self.name = name
        return self

class Dog(Animal):
    def set_breed(self, breed: str) -> Self:
        self.breed = breed
        return self

dog = Dog().set_name("Buddy").set_breed("Labrador")
# dog の型は Dog

Animal.set_name の返り値は Dog として推論されます。TypeVar を使った場合と同じ効果ですが、より簡潔です。

ファクトリメソッド

from typing import Self

class Config:
    def __init__(self, data: dict):
        self.data = data
    
    @classmethod
    def from_file(cls, path: str) -> Self:
        with open(path) as f:
            return cls(json.load(f))

class AppConfig(Config):
    pass

config = AppConfig.from_file("config.json")
# config の型は AppConfig

再帰的な型定義

from typing import Self

class Node:
    def __init__(self, value: int):
        self.value = value
        self.children: list[Self] = []
    
    def add_child(self, child: Self) -> Self:
        self.children.append(child)
        return self

Self 型により、メソッドチェーンやファクトリパターンの型付けが簡潔かつ正確になります。