在Python中是否可以修饰一个函数,以便记录它的开始和结束?

时间:2016-08-27 09:25:30

标签: python python-decorators

对于日志记录,我希望每个函数在函数的开头和结尾都记录自己的名称。

def my_function():
    print("Enter my_function")
    # ...
    print("Leave my_function")

如果我更改了该功能的名称,我也必须更新这些打印消息。我正在寻找一种自动化的方法。

def my_decorator(func):
    print("Enter ", func.__name__)
    func()
    print("Leave ", func.__name__)

def my_function():
    # do the work
    pass

# main:
my_decorator(my_function)

这可能是一个简单的方法,也许使用装饰器?如果my_function有参数,那会怎么样?

2 个答案:

答案 0 :(得分:8)

你是对的,使用装饰器是实现这种行为的完美方式。

你需要知道的是装饰器是如何工作的:它只是将一个函数作为参数,并返回另一个函数。这个其他返回函数用于包装你的参数函数。

def log_in_out(func):

    def decorated_func(*args, **kwargs):
        print("Enter ", func.__name__)
        result = func(*args, **kwargs)
        print("Leave ", func.__name__)
        return result

    return decorated_func

@log_in_out
def my_function():
    print("Inside my_function")
    return 42


val = my_function()

print(val)

# Output:
# Enter  my_function
# Inside my_function
# Leave  my_function
# 42

另请注意,@ŁukaszRogalski使用his anwser post的答案对于保留函数docstring非常有用。

最后,来自@MartinBonner的一个好主意是,您还可以使用它来记录函数中的错误:

def log_in_out(func):

    def decorated_func(*args, **kwargs):
        name = func.__name__
        print("Enter", name)
        try:
            result = func(*args, **kwargs)
            print("Leave", name)
        except:
            print("Error in", name)
            raise
        return result

    return decorated_func

请注意,我重新抛出错误,因为我认为应该从函数外部管理控制流。

对于更高级的日志记录,您应该使用提供许多功能的functools.wraps

答案 1 :(得分:1)

当然,对于星球和星球来说,它真的很简单。 functools.wraps用于将任何元数据(__name____doc__等)从输入函数重写为包装器。如果函数对象字符串表示对您来说过于冗长,则可以改为使用print("Enter", f.__name__)

import functools


def d(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        print("Enter", f)
        result = f(*args, **kwargs)
        print("Exit", f)
        return result
    return wrapper


@d
def my_func():
    print("hello")


@d
def my_func2(x):
    print(x)


my_func()
my_func2("world")

输出:

Enter <function my_func at 0x10ca93158>
hello
Exit <function my_func at 0x10ca93158>
Enter <function my_func2 at 0x10caed950>
world
Exit <function my_func2 at 0x10caed950>