【问题标题】:Python -- Function Decorator results in function being called twicePython——函数装饰器导致函数被调用两次
【发布时间】:2019-11-07 18:24:37
【问题描述】:

我在一些单元测试中使用函数装饰器来测试代码。但是,我发现这个装饰器会导致函数被调用两次(因此打印它的输出两次)。

我在某些函数返回错误的返回值后发现了这个错误,这种错误的返回值只有在函数被调用两次时才会发生。

#!/usr/bin/env python3

def decorate(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print("#" * 40)
        print("Testing function {}".format(func.__name__))
        print("Arguments passed: {} ".format(args))
        print("Begin output of {}".format(func.__name__))
        print("#" * 40)
        try:
            func(*args, **kwargs)
        except Exception as e:
            print("Error occured: {}".format(e))

        print("#" * 40)
        print("End of output of {}".format(func.__name__))
        print("#" * 40)
        print("\n" * 5)
        return func(*args,**kwargs) #Error happens on this line here
    return inner

#Add decorator to function definition. 
@decorate
def asdf():
    print("THIS SHOULD PRINT ONCE")

#Call function

asdf()

输出(与复制的间距完全相同):

########################################
Testing function asdf
Arguments passed: ()
Begin output of asdf
########################################
THIS SHOULD PRINT ONCE
########################################
End of output of asdf
########################################






THIS SHOULD PRINT ONCE

我想要的输出:

########################################
Testing function asdf
Arguments passed: ()
Begin output of asdf
########################################
THIS SHOULD PRINT ONCE
########################################
End of output of asdf
########################################

我正在尝试消除该函数的第二次调用。我知道我的错误在于装饰器,我只是找不到它。

【问题讨论】:

    标签: python python-3.x python-decorators


    【解决方案1】:

    请注意,您从装饰器内部调用函数两次:

    • 曾在try/except 块中。
    • 在 return 语句中一次。

    您可能希望将第一个调用(try 块中的那个)更改为:

    res = func(*args, **kwargs)
    

    然后简单地说:

    return res
    

    编辑:(根据@DanielRoseman 的评论)

    由于您没有从except 块提升或返回,您必须在那里(except 内)分配res,或者在装饰器的开头将其预定义为None (或任何适合您的程序的值)。

    【讨论】:

    • 虽然他们需要在 except 块中定义 res 的默认值,否则如果 can 引发异常则不会定义它
    【解决方案2】:

    确实,您打了两次电话,这里用粗体表示:

    def decorate(func):
        @wraps(func)
        def inner(*args, **kwargs):
            print("#" * 40)
            print("Testing function {}".format(func.__name__))
            print("Arguments passed: {} ".format(args))
            print("Begin output of {}".format(func.__name__))
            print("#" * 40)
            try:
                func(*args,**kwargs)
            except Exception as e:
                print("Error occured: {}".format(e))
    
            print("#" * 40)
            print("End of output of {}".format(func.__name__))
            print("#" * 40)
            print("\n" * 5)
            return func(*args,**kwargs)
        return inner

    您可能希望省略两者之一,并将结果存储在临时变量中,例如:

    def decorate(func):
        @wraps(func)
        def inner(*args, **kwargs):
            print("#" * 40)
            print("Testing function {}".format(func.__name__))
            print("Arguments passed: {} ".format(args))
            print("Begin output of {}".format(func.__name__))
            print("#" * 40)
            try:
                result = func(*args,**kwargs)
            except Exception as e:
                print("Error occured: {}".format(e))
                result = None
    
            print("#" * 40)
            print("End of output of {}".format(func.__name__))
            print("#" * 40)
            print("\n" * 5)
            return result
        return inner

    【讨论】:

    • 谢谢!我知道返回函数的方式有误,只是不确定如何返回。
    猜你喜欢
    • 2020-04-28
    • 2017-12-11
    • 2019-08-02
    • 2021-09-10
    • 2017-01-15
    • 1970-01-01
    • 1970-01-01
    • 2017-08-01
    • 1970-01-01
    相关资源
    最近更新 更多