Рассмотрим этот цикл:
for name, func in inspect.getmembers(targetCls, inspect.ismethod):
def wrapper(*args, **kwargs):
print ("Start debug support for %s.%s()" % (targetCls.__name__, name))
Когда в конечном итоге вызывается wrapper
, он ищет значение name
.Не найдя его в locals (), он ищет его (и находит его) в расширенной области действия for-loop
.Но к тому времени for-loop
закончился, и name
относится к последнему значению в цикле, то есть TestMethod2
.
Так что оба раза вызывается обертка, name
оценивается как TestMethod2
.
Решение состоит в том, чтобы создать расширенную область, где name
привязано к правильному значению.Это можно сделать с помощью функции closure
со значениями аргументов по умолчанию.Значения аргументов по умолчанию оцениваются и фиксируются во время определения и привязываются к переменным с тем же именем.
def Debug(targetCls):
for name, func in inspect.getmembers(targetCls, inspect.ismethod):
def closure(name=name,func=func):
def wrapper(*args, **kwargs):
print ("Start debug support for %s.%s()" % (targetCls.__name__, name))
result = func(*args, **kwargs)
return result
return wrapper
setattr(targetCls, name, closure())
return targetCls
В комментариях eryksun предлагает еще лучшее решение:
def Debug(targetCls):
def closure(name,func):
def wrapper(*args, **kwargs):
print ("Start debug support for %s.%s()" % (targetCls.__name__, name));
result = func(*args, **kwargs)
return result
return wrapper
for name, func in inspect.getmembers(targetCls, inspect.ismethod):
setattr(targetCls, name, closure(name,func))
return targetCls
Теперь closure
нужно анализировать только один раз.Каждый вызов closure(name,func)
создает свою собственную область функций с различными значениями для name
и func
, которые связаны правильно.