【Python】Mixinクラスの継承先でインスタンス変数の定義を強制したい

Mixinクラスのメソッドの中で、インスタンス変数にアクセスしたいとします。

例えば、インスタンス変数nameにアクセスするMixinクラスHelloMixinを考えます。

class HelloMixin:
    def hello(self):
        return f"hello {self.name}"

HelloMixinを継承した、MyClassWithHelloクラスを考えます。

class MyClassWithHello(HelloMixin):
    def __init__(self, name):
        self.name = name

    def use_hello(self):
        msg = self.hello()
        print(msg)


my_class_with_hello = MyClassWithHello("Alice")
my_class_with_hello.use_hello()  # hello Alice

__init__関数内でインスタンス変数nameを定義しています。


ここで、うっかりインスタンス変数nameの定義を忘れてしまったとします。

class HelloMixin:
    def hello(self):
        return f"hello {self.name}"


class MyClassWithHello(HelloMixin):
    def __init__(self):
        pass

    def use_hello(self):
        msg = self.hello()
        print(msg)


my_class_with_hello = MyClassWithHello()
my_class_with_hello.use_hello()  # AttributeError: 'MyClassWithHello' object has no attribute 'name'

use_hello関数を実行すると、nameが見つからないよと怒られます。


というわけで、Mixinクラスを継承したクラスで、インスタンス変数の定義を強制したくなります。

どうしようか悩んでいたのですが、以下のような実装を思いつきました。

  • MixinクラスHelloMixinを抽象基底クラス(ABC)にする
  • MixinクラスHelloMixin内で、@propertyかつ@abstractmethodname関数を定義する
from abc import ABC, abstractmethod


class HelloMixin(ABC):
    @property
    @abstractmethod
    def name(self):
        pass

    def hello(self):
        return f"hello {self.name}"


class MyClassWithHello(HelloMixin):
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

    def use_hello(self):
        msg = self.hello()
        print(msg)


my_class_with_hello = MyClassWithHello("Alice")
my_class_with_hello.use_hello()

HelloMixin内で@propertyかつ@abstractmehodname関数を定義しています。

そのため、継承先のMyClassWithHelloではname関数の実装が必須となります。

よって、Mixinクラスの継承先でインタンス変数の定義を強制することができました。

もしname関数の実装を忘れると、オブジェクト作成時に以下のエラーを送出してくれます。

TypeError: Can't instantiate abstract class MyClassWithHello with abstract methods name


…というわけで、やりたいことは達成できたのですが、なんかボイラープレート感…