Python 重复使用不正确的打印
我有以下简单代码(代表更大的代码): 如果只有总和(1,3)行在运行,我得到(如预期的): 如果只有Python 重复使用不正确的打印,python,decorator,Python,Decorator,我有以下简单代码(代表更大的代码): 如果只有总和(1,3)行在运行,我得到(如预期的): 如果只有多(2,3)行操作,我从求和函数中得到了这些恼人的“剩菜”: $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is Start of FUNC___ 'multi' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$* $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___T
多(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' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
所以问题是,如果我使用一个decorator,它使用的函数/方法也有相同的decorator,它会打印出内部函数的无用数据
我希望看到的是:
$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is Start of FUNC___ 'multi' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
6
$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is End of FUNC___ 'multi' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
有多个修饰函数,每个函数都有自己独立的
wrap\u func
function对象。这些是独立的
如果必须围绕多个相互调用的修饰函数生成一组行,则需要保持共享堆栈计数;将此信息附加到装饰器,而不是装饰器返回的包装:
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.\u stack\u count
设置为1,之后对包装器的任何后续调用只会进一步增加该数字,而不会打印更多。返回时,计数器再次递减(重新使用该堆栈级别的旧的、未递增的值),只有当该值再次为0时,我们才能再次打印
请注意,我使用了try…finally
,以确保即使修饰函数引发异常,堆栈计数器也会递减
演示:
跟踪这样的堆栈确实是一个上下文管理器类型的问题,因此我将在这样一个上下文管理器中进一步总结:
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
有多个修饰函数,每个函数都有自己独立的
wrap\u func
function对象。这些是独立的
如果必须围绕多个相互调用的修饰函数生成一组行,则需要保持共享堆栈计数;将此信息附加到装饰器,而不是装饰器返回的包装:
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.\u stack\u count
设置为1,之后对包装器的任何后续调用只会进一步增加该数字,而不会打印更多。返回时,计数器再次递减(重新使用该堆栈级别的旧的、未递增的值),只有当该值再次为0时,我们才能再次打印
请注意,我使用了try…finally
,以确保即使修饰函数引发异常,堆栈计数器也会递减
演示:
跟踪这样的堆栈确实是一个上下文管理器类型的问题,因此我将在这样一个上下文管理器中进一步总结:
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
您可以使用某种全局状态锁来指示堆栈中的一个包装器是否已经打印。此锁将由最外层的包装器获取,并防止内部包装器打印 代码示例:
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
dec
的缩进显然是错误的。请验证我更正它的尝试是否反映了您的实际代码。如果您装饰函数,则会丢失未装饰的表单。“你也不能简单地说出你应该给哪个打电话。”丹尼·卡明斯基,尽量让问题简单易懂。除非确实需要,否则滚动代码有点困难。dec
的缩进显然是错误的。请验证我更正它的尝试是否反映了您的实际代码。如果您装饰函数,则会丢失未装饰的表单。“你也不能简单地说出你应该给哪个打电话。”丹尼·卡明斯基,尽量让问题简单易懂。除非确实需要,否则滚动代码有点困难。锁是二进制的,因此无法处理超过2级的调用。@据我所知,MartijnPieters OP只想打印最外层的函数调用。所以二进制文件就足够了,因为它可以防止打印所有内部包装函数调用。不可以,因为您在第一次返回时将锁设置为false。您需要一个计数器来跟踪您在堆栈中的深度。@MartijnPieters我只将它设置为False
,如果它以前没有设置过(因此被获取)。只有最外层的函数在计算了所有最内层的函数后才会将其设置为False
(因为已经设置了锁,所以不会触及锁)。实际上,我们只需要知道装饰器(最外层)是否被激活。这是二进制的东西。拥有一个计数器,您也只能检查计数器>0
这是二进制的。您也可以在持有锁的同时进行调用。锁是二进制的,因此无法处理超过2级的调用。@MartijnPieters据我所知,OP只想打印最外层的函数调用。所以二进制文件就足够了,因为它可以防止打印所有内部包装函数调用。不可以,因为您在第一次返回时将锁设置为false。您需要一个计数器来跟踪您在堆栈中的深度。@MartijnPieters我只将它设置为False
,如果它以前没有设置过(因此被获取)。只有最外层的函数在计算了所有最内层的函数后才会将其设置为False
(因为已经设置了锁,所以不会触及锁)。实际上,我们只需要知道装饰器(最外层)是否被激活。这是二进制的东西。拥有一个计数器,您还可以只检查二进制的计数器>0
。您还可以在持有锁的同时进行调用。顺便问一下,如果在多个线程中使用dec
,线程安全性如何?顺便问一下,如果在多个线程中使用dec
,线程安全性如何?