[英]Re-use incorrect printing
我有以下简单代码(代表更大的代码):
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)
行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' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
您具有多个修饰的函数,并且每个函数都具有自己的独立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
使用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
您可以使用某种全局状态锁来指示堆栈中的包装器之一是否已经打印。 此锁将由最外面的包装纸获取,并防止打印内部的包装纸。
代码示例:
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
或者:
lock = False
def dec(f):
def wrapper(*args, **kwargs):
global lock
locked = lock
if not locked:
lock = True
print('Start', f.__name__)
result = f(*args, **kwargs)
if not locked:
lock = False
print('End', f.__name__)
return result
return wrapper
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.