デフォルトで None
に設定されている引数 p
を取る関数 f
があるとします:
def f(p = None): ...
p
が指定されている場合、特定のメンバー関数を持つクラス C
のオブジェクトであることが期待されます。これにより、次のようなコードが繰り返されます
p が None でない場合: p.f()
これは面倒でエラーが発生しやすい作業です。 p
のデフォルトとして None
を選択する代わりに、すべてのメンバ関数が何もしない C
のクローンが必要です。その後、次のように記述できます
def f(p = Cc()):
p.f()
...
Cc
は C
のクローンです。
p
が複製されたクラスのオブジェクトである場合、呼び出しは何もしません。p
が呼び出し元によって提供されている場合、対応する f
が呼び出されます。
このようなクローンを自動的に作成するにはどうすればよいでしょうか?
この問題は、次のようなデコレータ クラスを定義することで解決できます:
import inspect
クラスMockDec:
def __init__(自己、ベース):
self.base_func_names = {v[0] for v in inspect.getmembers(base, predicate=inspect.isfunction)
そうでない場合 v[0].startswith('__')}
def __call__(self, cls):
def pss(*argc, **argv):
合格
cls_func_names = {v[0] for v in inspect.getmembers(cls, predicate=inspect.isfunction)
そうでない場合 v[0].startswith('__')}
for n in self.base_func_names - cls_func_names:
setattr(cls、n、pss)
cls を返す
ここで base_func_names
と cls_func_names
は、base
と cls
で定義されたすべての関数名を含む Python セットです。 for
ループ関数が cls
に追加され、これらが pss
を呼び出すだけで、何もしません。式 self.base_func_names - cls_func_names
は設定の違いです。したがって、base
で定義された関数は、cls
で定義されていない場合にのみ cls
に追加されます。
デコレータの使用例を次に示します。最初にクラス C1
を定義します:
クラス C1:
def __init__(自己):
print( "C1.__init__" )
def f1(自己、私):
print( f"C1.f1({i})" )
def f2(自己、私):
print( f"C1.f2({i})" )
MockDec
を使用して C1
のクローンを作成します:
@MockDec(base=C1)
クラス C2:
def __init__(自己):
print( "C2.__init__" )
def f2(自己、私):
print( f"C2.f2({i})" )
c1 = C1()
c1.f1(7)
c1.f2(9)
c2 = C2()
c2.f1(ナンセンス="無") #1
c2.f2(19) #2
これにより出力が得られます
C1.__init__
C1.f1(7)
C1.f2(9)
C2.__init__
C2.f2(19)
明らかに、C2
は #1
で呼び出されたときに何もしない関数 f1
を提供します。 #2
に示すように、f2
の定義はデコレータによって変更されません。