我有以下简单代码(代表更大的代码):
def dec(data):
def wrap_func(*args, **kwargs):
if not wrap_func.has_run: print(
'\n$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is Start of FUNC___ \'{}\' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*\n'.format(
data.__name__))
print(data(*args, **kwargs))
if not wrap_func.has_run: print(
'\n$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is End of FUNC___ \'{}\' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*\n'.format(
data.__name__))
wrap_func.has_run = True
# return result
wrap_func.has_run = False
return wrap_func
@dec
def sum(a=1, b=1, times=1):
return (a + b) * times
@dec
def multi(a=2, b=3):
return sum(a, b=0, times=b)
# sum(1, 3)
multi(2,3)
如果只有总和(1,3)行正在运行,我得到(按预期):
$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is Start of FUNC___ 'sum' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
4
$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is End of FUNC___ 'sum' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
如果仅multi (2,3)
操作的行我从求和函数中得到这些烦人的'剩余':
$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is Start of FUNC___ 'multi' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is Start of FUNC___ 'sum' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
6
$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is End of FUNC___ 'sum' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
None
$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is End of FUNC___ 'multi' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
所以问题是,如果我使用的装饰器使用的函数/方法也有相同的装饰器,它会打印出内部函数的无用数据
我希望看到的是:
$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is Start of FUNC___ 'multi' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
6
$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is End of FUNC___ 'multi' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
答案 0 :(得分:2)
你有多个修饰函数,每个函数都有自己独立的wrap_func
函数对象。这些是独立的。
如果必须围绕多个相互调用的函数生成一组线,则需要保持共享堆栈数;将此信息附加到装饰器,而不是装饰器返回的包装器:
def dec(f):
def wrapper(*args, **kwargs):
stack_count = getattr(dec, '_stack_count', 0)
if stack_count == 0:
print(f'-- start of decorated functions -- {f.__name__}')
dec._stack_count = stack_count + 1
try:
result = f(*args, **kwargs)
if result: print(result)
finally:
dec._stack_count = stack_count
if stack_count == 0:
print(f'-- end of decorated functions -- {f.__name__}')
# return result
return wrapper
因此,对包装器的第一次调用将dec._stack_count
设置为1,之后对包装器的任何后续调用将仅进一步增加该数字,并且不再打印任何内容。返回时,计数器再次递减(重新使用该堆栈级别的旧的,未递增的值),并且只有当该值再次为0时,我们才会再次打印。
请注意,我使用try...finally
来确保堆栈计数器递减,即使装饰函数引发了异常。
演示:
>>> @dec
... def sum(a=1, b=1, times=1):
... return (a + b) * times
...
>>> @dec
... def multi(a=2, b=3):
... return sum(a, b=0, times=b)
...
>>> multi(2, 3)
-- start of decorated functions -- multi
6
-- end of decorated functions -- multi
>>> sum(2, 3)
-- start of decorated functions -- sum
5
-- end of decorated functions -- sum
跟踪这样的堆栈实际上是一个上下文管理器类型的问题,所以我在这样的上下文管理器中进一步包装它:
from contextlib import contextmanager
@contextmanager
def print_outer(before, after):
"""Context manager that prints the before and after text only for the outermost call
This is a reentrant context manager, and is not thread-safe.
"""
outer = getattr(print_outer, '_is_outermost', True)
if outer:
print_outer._is_outermost = False
print(before)
try:
yield
finally:
if outer:
print_outer._is_outermost = True
print(after)
然后在装饰器中使用此上下文管理器:
def dec(f):
def wrapper(*args, **kwargs):
banners = (
f'-- start of decorated functions -- {f.__name__}',
f'-- end of decorated functions -- {f.__name__}'
)
with print_outer(*banners):
result = f(*args, **kwargs)
if result: print(result)
# return result
return wrapper
答案 1 :(得分:1)
您可以使用某种全局状态锁定,指示堆栈中的某个包装器是否已经打印。此锁定将由最外层的包装器获取,并防止内部包装打印。
代码示例:
localStorage
可替换地:
lock = False
def dec(f):
def wrapper(*args, **kwargs):
global lock
if not lock:
lock = True
print('Start', f.__name__)
result = f(*args, **kwargs)
print('End', f.__name__)
lock = False
else:
result = f(*args, **kwargs)
return result
return wrapper