繁体   English   中英

重新使用错误的打印

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM