6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Python】派生クラスを動的に取得して処理をぶん回したい

Posted at

はじめに

ゴールデンウィークをだらだら過ごしていたらふと気になりました1
実際どういうことかを「各チェックを行うクラス」を例に見ていきましょう。

概要

各チェックを行う元になる抽象基底クラスCheckBaseを下記のように定義します。

check.py
from abc import ABC, abstractmethod

class CheckBase(ABC):
    """各チェックIDのベースとなる抽象基底クラス"""

    def __init__(self, data):
        self.data = data

    @abstractmethod
    def is_target(self) -> bool:
        """チェック対象であればTrueを返す"""
        ...

    @abstractmethod
    def check(self) -> None:
        """チェックを行う"""
        ...

このCheckBaseを継承した各チェックを定義します。

check.py
class CheckID1(CheckBase):
    def is_target(self) -> bool:
        return True

    def check(self) -> None:
        print("チェックID1が実行された")

class CheckID2(CheckBase):
    def is_target(self) -> bool:
        return True

    def check(self) -> None:
        print("チェックID2が実行された")

このCheckBaseを継承したCheckID1CheckID2のインスタンスを作成して処理をぶん回したいとします。
この実行を行うmain.pyは下記のように書くことにします。

main.py
import check

# 呼び出し部分だけ書く
for cls_ in [check.CheckID1, check.CheckID2]:
    check_cls = cls_(check_data)
    if check_cls.is_target():
        check_cls.check()

このように書いてしまうと下記のような問題点に遭遇します。

  • CheckIDが増えていくと、[check.CheckID1, check.CheckID2]も拡大していく
  • CheckIDを実装したら、呼び出し側のmain.pyの改修が必要になる

この2つを解決するためには、CheckBaseを継承した派生クラスが動的に取得出来れば良さそうです。

派生クラスを動的に取得する

組み込みモジュールである inspect モジュールを使います。

下記に CheckBase を継承した派生クラスだけを取得するメソッドを示します。
ざっくりやっていることは下記の2点です(難しいことはやっていないので詳しい説明は省略)。

  1. get_all_check_cls が定義されているモジュール内の全てのクラスを取得する
     → 今回の場合はcheck.pyに定義することにする
  2. CheckBase の派生クラスであり CheckBase 自身ではないクラスを収集する
import sys
import inspect

def get_all_check_cls() -> list[type[CheckBase]]:
    """CheckBaseを継承した全てのクラスを取得する"""
    ret = []
    module = sys.modules[__name__]
    # クラス名(CheckBase, CheckID1など)とそのオブジェクトがイテレートされる(今回クラス名は使わないので`_`にしている)
    for _, obj in inspect.getmembers(module, inspect.isclass):
        if issubclass(obj, CheckBase) and obj is not CheckBase:
            ret.append(obj)
    return ret

呼び出し側のmain.pyは、このget_all_check_clsを呼び出すだけで済むので、新しいチェックが増えてもmain.pyに変更が生じないので嬉しくなれます。

  1. だらだらしてたらゴールデンウィークが終わってしまって、こんな中途半端な時期に投稿することになってしまった…

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?