メソッド名一覧の表示(2)

以下を修正。

  • cls.__bases__で、1つ上の階層のベースクラスしかとれていなかったので、再帰的にベースクラスを探すようにした。
  • 継承階層で同じ名称のメソッドをオーバーライドしていた場合、一番子供側のメソッドを呼び出すようにした。
  • 新型クラスに対して、 isinstance(obj, types.InstanceType) と isinstance(obj, types.ClassType) の両方ともFalseになってしまって対応していなかったので対応した。
def call_tests(obj, single_level=False, call_static=True):
    if hasattr(obj, '__bases__'):
        # class object
        cls = obj
        obj = obj()  # obj is bound to instance object.
    else:
        if not hasattr(obj, '__class__'): return  # non class type
        # instance object
        cls = obj.__class__
    
    # make dict of attributes
    bases = []
    def base_classes(cls):
        if cls.__bases__:
            bases.extend(cls.__bases__)
            for base in cls.__bases__:
                base_classes(base)
    base_classes(cls)
    
    names = {}
    names.update(cls.__dict__)
    for base in bases:
        for attr, val in base.__dict__.items():
            if not names.has_key(attr):
                names[attr] = val
    
    if single_level:
        for base in bases:
            for name in base.__dict__:
                if names.has_key(name):
                    del names[name]
    
    # call function of attributes
    for name in sorted(names):
        if name[:5] == 'test_':
            if call_static:
                f = getattr(obj, name)
                if callable(f): f()
            else:
                f = names[name]
                if callable(f): f(obj)

クラスおよびインスタンスの判定、クラスorインスタンス以外の型の判定を特定の属性を持っているかで判定しているが、それしかないのだろうか。typesで新型クラスが対応していないのは問題だと思う。あと、インスタンスメソッドとクラスメソッドを判定する方法も用意してくれていてもよさそう。Pythonはリフレクションがあまりやり易いとは言えないと思う。


汎用性を考えるなら、以下のような関数を作成して、呼び出し側で'test_'のメソッドを検索して呼び出した方が良いかもしれない。

def get_methods(obj, single_level=False):
    ...
    return instance_methods, class_methods  # 関数オブジェクトのリストのタプルを返す

funcs1, funcs2 = get_methods(A)
for func in funcs1:
    if func.__name__ == 'test_':
       func()