简体   繁体   English

重新使用错误的打印

[英]Re-use incorrect printing

I have the following simple code (which represents much larger code): 我有以下简单代码(代表更大的代码):

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)

If only the line of sum (1,3) are operating I get (as intended): 如果仅求和线(1,3)在运行,我将得到(如预期的那样):

$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is Start of FUNC___ 'sum' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*
4
$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is End of FUNC___ 'sum' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*

If only the line of multi (2,3) operating I get these annoying 'leftovers' from the summing function: 如果只有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' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*

So the issue is, if I use a decorator which uses a function/method which has also the same decorator, it prints me the useless data of the internal function 因此,问题是,如果我使用的装饰器使用的功能/方法也具有相同的装饰器,则它会打印出内部函数的无用数据

What I wish to see is: 我希望看到的是:

$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is Start of FUNC___ 'multi' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*

6

$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*___This is End of FUNC___ 'multi' $*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*$*

You have multiple decorated functions, and each function is given it's own independent wrap_func function object. 您具有多个修饰的函数,并且每个函数都具有自己的独立wrap_func函数对象。 These are independent. 这些是独立的。

If you must produce only one set of lines around multiple decorated functions calling each other, you'd need to keep a shared stack count; 如果您必须在多个相互调用的修饰函数之间仅产生一组行,则需要保持共享的堆栈数; attach this information to the decorator , not the wrapper that the decorator returns: 将此信息附加到装饰器 ,而不是装饰器返回的包装器:

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

So the first call to the wrapper sets dec._stack_count to 1, after which any subsequent calls to wrappers will only further increment that number and nothing more is printed. 因此,对包装程序的第一次调用dec._stack_count设置为1,此后对包装程序的任何后续调用只会进一步增加该数字,并且不再打印任何内容。 On return, the counter is decremented again (the old, un-incremented value for that stack level is re-used), and only when that value is 0 again do we print again. 返回时,计数器再次递减(该堆栈级别的旧的,未递增的值被重新使用),并且只有当该值再次为0时,我们才再次打印。

Note that I used try...finally to ensure the stack counter is decremented even if the decorated function raised an exception. 请注意,即使修饰后的函数引发异常,我还是try...finally使用try...finally以确保堆栈计数器递减。

Demo: 演示:

>>> @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

Keeping track of a stack like that is really a context manager type of problem, so I'd further wrap this in such a context manager: 跟踪这样的堆栈实际上是上下文管理器类型的问题,因此我将其进一步包装在这样的上下文管理器中:

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)

then use this context manager in the decorator: 然后在装饰器中使用此上下文管理器:

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

You can use some kind of global state lock which indicates whether one of the wrappers in the stack has printed already. 您可以使用某种全局状态锁来指示堆栈中的包装器之一是否已经打印。 This lock will be acquired by the outermost wrapper and prevents the inner ones from printing. 此锁将由最外面的包装纸获取,并防止打印内部的包装纸。

Code example: 代码示例:

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

Alternatively: 或者:

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