はじめに
Pythonでは、呼び出し可能な関数ライクなオブジェクトをいくつかの方法で作成することができる。
- 関数として定義
- ラムダとして定義
- クラスにメソッドとして定義
__call__()
を実装することで定義- (他にもあるかもしれない)
ここで、デバッグやソースコードの解析のために、実行時にできるだけ正確に、どの方法で作成されたかを知りたいという状況になった。
単純にisinstance(x, Callable)
するだけだと、x
が上記のどれの場合も真になってしまうので、別の方法を考える必要が出てきた。
環境
$ python3 --version Python 3.10.12
検査の方法
それぞれ個別に判別するため、以下の検査用の関数を作成してみた。
isinstance(x, FunctionType)
だけでは関数かラムダかを区別できないので、__name__
も検査する必要がある。
ちなみにtypes.FunctionType
と同様にtypes.LambdaType
があるものの、これは実質的にtypes.FunctionType
の別名であり、ラムダかどうかの判別には利用できない。
from typing import Callable from types import FunctionType, MethodType def is_function(x) -> bool: return isinstance(x, FunctionType) and (x.__name__ != '<lambda>') def is_method(x) -> bool: return isinstance(x, MethodType) and (x.__name__ != '<lambda>') def is_lambda(x) -> bool: return (isinstance(x, FunctionType) or isinstance(x, MethodType)) and (x.__name__ == '<lambda>') def is_call(x) -> bool: return isinstance(x, Callable) and (not isinstance(x, FunctionType)) and (not isinstance(x, MethodType))
実行結果
以下のテストコードを実行してみる。
# 関数として定義 def no1_func(x): return x # ラムダとして定義 no2_lambda = lambda x: x class Functor: def __init__(self) -> None: # ラムダをメンバーとして定義 self.no3_lambda = lambda x: x # メソッドとして定義 def no4_method(self, x): return x # 関数オブジェクトとして定義 def __call__(self, x): return x obj = Functor() funcs = { 'no1_func': no1_func, 'no2_lambda': no2_lambda, 'no3_lambda': obj.no3_lambda, 'no4_method': obj.no4_method, 'no5_functor': obj, } for name, func in funcs.items(): print(f'{name:11s} ... ', end='') print(f'function: {is_function(func):1d}, ', end='') print(f'lambda: {is_lambda(func):1d}, ', end='') print(f'method: {is_method(func):1d}, ', end='') print(f'__call__: {is_call(func):1d}', end='') print()
実行結果は以下の通り。
no1_func ... function: 1, lambda: 0, method: 0, __call__: 0 no2_lambda ... function: 0, lambda: 1, method: 0, __call__: 0 no3_lambda ... function: 0, lambda: 1, method: 0, __call__: 0 no4_method ... function: 0, lambda: 0, method: 1, __call__: 0 no5_functor ... function: 0, lambda: 0, method: 0, __call__: 1