指定したモジュール配下から、特定クラスのサブクラスを探す

  • お題 : 「a.b モジュール配下にある a.Base のサブクラスのリストを表示したい」

Seasar や Spring のコンポーネントの自動登録みたいなイメージで。ふと思い立って python で書いてみました。

ファイルの構成は以下の通りで、該当の処理を x.py の中に書く事にします。

- __init__.py
- x.py
- a 
  +- __init__.py
  +- b 
     +- __init__.py
     +- c 
        +- __init__.py

クラスは各々の __init__.py で以下のように定義してあるます。

  • a.Base
  • a.A (extends a.Base)
  • a.Not
  • a.b.B (extends a.Base)
  • a.b.Not
  • a.b.c.C (extends a.Base)
  • a.b.c.Not

ここから、a.b 配下で a.Base のサブクラスの a.b.B と a.b.c.C を探すコードはこんな感じで。

# -*- coding: utf-8 -*-
from types import ClassType
import os

def traversal(module_name,clazz):
    mod = __import__(module_name, {}, {}, [''])
    for name in dir(mod):
        name_obj = mod.__dict__[name]
        if type(name_obj) in [ClassType,type]:
            if issubclass(name_obj,clazz) and name_obj != clazz :
                print name_obj
    # child
    moddir = os.path.dirname(mod.__file__)
    for child in os.listdir(moddir):
        p = os.path.join(moddir,child)
        if os.path.isdir(p) :
            traversal(".".join([module_name,child]),clazz)

if __name__ == "__main__" :
    import a
    traversal("a.b",a.Base)

緩いコードとはいえ、こういうことするの楽ですね、特に変わったもの使わないですし。もっと簡単に書く方法ありそうですが。

余談ですが、GAE/P の webapp.template.register_template から呼ばれるdjango.template の中の get_library も似たような処理してました。register って、まま名前でアクセスしてますね。この辺り大胆だなぁ、とも感じます。

def get_library(module_name):
    lib = libraries.get(module_name, None)
    if not lib:
        try:
            mod = __import__(module_name, {}, {}, [''])
        except ImportError, e:
            raise InvalidTemplateLibrary, "Could not load template library from %s, %s" % (module_name, e)
        try:
            lib = mod.register
            libraries[module_name] = lib
        except AttributeError:
            raise InvalidTemplateLibrary, "Template library %s does not have a variable named 'register'" % module_name
    return lib