python

しょっちゅう忘れることを書いておく。

33

306 views

クラスが依存するクラスは引数でもらうタイプの実装方法。
ミソなのはinterfaceを介するクラスを前提とすることで、期待していないクラスを指定できないようにできる。
下の例で言えば、引数に渡されるクラス、tearcherとstudentにはstand_upが定義されていることを期待している。

基本的な書き方は以下。
TyoureiControllerの__init__で、isinnstanceでチェックを行い、interfaceが継承されているクラスかどうかをチェックする。
javaとかだとコンパイルエラーで弾けるけど、pythonは動かすまでわからないところがちょっと残念だけど、仕方なし。
PycharmなどのIDEがあれば、エラー表示はしてくれる。

# coding: UTF-8
from abc import ABC, ABCMeta, abstractmethod
import inject


class PersonInterface(ABC):
    """
    人クラスのインターフェース
    """
    @abstractmethod
    def stand_up(self):
        print("立ち上がりました")


class Student(PersonInterface):
    def __init__(self):
        pass

    def stand_up(self):
        # override
        print("しゃんと立ち上がりました")


class Teacher(PersonInterface):
    def __init__(self):
        pass

    def stand_up(self):
        # override
        print("腰が重そうに立ち上がりました")


class TyoureiController:

    def __init__(self, teacher: PersonInterface, student: PersonInterface):
        if not isinstance(teacher, PersonInterface):
            raise Exception("teacherがPersonInterfaceではありません")

        if not isinstance(student, PersonInterface):
            raise Exception("studentがPersonInterfaceではありません")

        self.teacher = teacher
        self.student = student

    def start(self):
        self.teacher.stand_up()
        self.student.stand_up()


def main():
    teacher = Teacher()
    student = Student()

    con = TyoureiController(teacher, student)
    con.start()


if __name__ == '__main__':
    main()

ただ、この書き方だとTyoureiControllerにteacher,student以外にも、parentやPTAなどの渡すクラスが増えたときに引数が増えて汚いと思う人もいる。
そんな人はinjenctを使ってまとめて渡すことができる。

と思ってサンプルを作ったが、これはエラーになる。

# coding: UTF-8
from abc import ABC, ABCMeta, abstractmethod
import inject


class PersonInterface(ABC):
    """
    人クラスのインターフェース
    """
    @abstractmethod
    def stand_up(self):
        print("立ち上がりました")


class Student(PersonInterface):
    def __init__(self):
        pass

    def stand_up(self):
        # override
        print("しゃんと立ち上がりました")


class Teacher(PersonInterface):
    def __init__(self):
        pass

    def stand_up(self):
        # override
        print("腰が重そうに立ち上がりました")


class TyoureiController:
    @inject.params(teacher=PersonInterface, student=PersonInterface)
    def __init__(self, teacher: PersonInterface, student: PersonInterface):
        if not isinstance(teacher, PersonInterface):
            raise Exception("teacherがPersonInterfaceではありません")

        if not isinstance(student, PersonInterface):
            raise Exception("studentがPersonInterfaceではありません")

        self.teacher = teacher
        self.student = student

    def start(self):
        self.teacher.stand_up()
        self.student.stand_up()


def inject_config(binder):
    binder.bind(PersonInterface, Teacher())
    binder.bind(PersonInterface, Student())

def main():
    #teacher = Teacher()
    #student = Student()

    inject.configure(inject_config)
    con = TyoureiController()
    con.start()


if __name__ == '__main__':
    main()

エラーメッセージは以下。

    raise InjectorException('Duplicate binding, key=%s' % cls)
inject.InjectorException: Duplicate binding, key=<class '__main__.PersonInterface'>

同じインターフェースを継承していることが原因。

こうすると直る。

# coding: UTF-8
from abc import ABC, ABCMeta, abstractmethod
import inject


class PersonInterface(ABC):
    """
    人クラスのインターフェース
    """
    @abstractmethod
    def stand_up(self):
        print("立ち上がりました")


class DummyInterface(ABC):
    @abstractmethod
    def stand_up(self):
        print("ダミーが立ち上がりました")

class Student(DummyInterface):
    def __init__(self):
        pass

    def stand_up(self):
        # override
        print("しゃんと立ち上がりました")


class Teacher(PersonInterface):
    def __init__(self):
        pass

    def stand_up(self):
        # override
        print("腰が重そうに立ち上がりました")


class TyoureiController:
    @inject.params(teacher=PersonInterface, student=DummyInterface)
    def __init__(self, teacher: PersonInterface, student: DummyInterface):
        if not isinstance(teacher, PersonInterface):
            raise Exception("teacherがPersonInterfaceではありません")

        if not isinstance(student, DummyInterface):
            raise Exception("studentがPersonInterfaceではありません")

        self.teacher = teacher
        self.student = student

    def start(self):
        self.teacher.stand_up()
        self.student.stand_up()


def inject_config(binder):
    binder.bind(PersonInterface, Teacher())
    binder.bind(DummyInterface, Student())

def main():
    #teacher = Teacher()
    #student = Student()

    inject.configure(inject_config)
    con = TyoureiController()
    con.start()


if __name__ == '__main__':
    main()

ようするにstudentのinterfaceをPersonInterfaceからDummyInterfaceに変更した。
これが良いのか悪いのかはちょっと微妙。
同じインターフェースだけど違うクラスを指定したときはエラーというのはDIの原則とかでエラーなのか、pythonのプログラムとしてただできないだけの問題なのかがわからない。

Page 38 of 56.

前のページ 次のページ



[添付ファイル]


お問い合わせ

プロフィール

マッスル

自己紹介

本サイトの作成者。
趣味:プログラム/水耕栽培/仮想通貨/激辛好き
プログラムは趣味と勉強を兼ねて、のんびり本サイトを作っています。
フレームワークはdjango。
仮想通貨はNEMが好き。
水耕栽培は激辛好きが高じて、キャロライナ・リーパーの栽培にチャレンジ中。

サイト/ブログ

https://www.osumoi-stdio.com/pyarticle/

ツイッター

@darkimpact0626