python装饰器functools wraps

2019-07-19  本文已影响0人  bonnie_xing

引自:https://www.cnblogs.com/myd7349/p/how_to_use_wraps_of_functools.html

一、wraps装饰器使用

1、通过trace装饰器,打印函数调用内容(未使用wraps)

# coding: utf-8
# Filename: decorator_wraps_test.py
import sys

debug_log = sys.stderr

def trace(func):
    if debug_log:
        def callf(*args, **kwargs):
            """A wrapper function."""
            debug_log.write('Calling function: {}\n'.format(func.__name__))
            res = func(*args, **kwargs)
            debug_log.write('Return value: {}\n'.format(res))
            return res
        return callf
    else:
        return func

@trace
def square(x):
    """Calculate the square of the given number."""
    return x * x

if __name__ == '__main__':
    print(square(3))

输出:
Calling function: square
Return value: 9
9

2、查看实际调用函数的内容

if __name__ == '__main__':
    help(square)
    print(square.__name__)
    print('debug_log' in square.__globals__)
    print('sys' in square.__globals__)

输出
callf(*args, **kwargs)
    A wrapper function.

callf
True
True

与预期不一致,期待返回的是square函数的信息
实际返回的是装饰器trace返回的callf函数信息

如何解决这个问题呢,那么就需要使用functools的wraps

3、通过wraps实现,展示真实的函数调用

# coding: utf-8
# Filename: decorator_wraps_test.py
import functools
import sys

debug_log = sys.stderr

def trace(func):
    if debug_log:
        @functools.wraps(func)
        def callf(*args, **kwargs):
            """A wrapper function."""
            debug_log.write('Calling function: {}\n'.format(func.__name__))
            res = func(*args, **kwargs)
            debug_log.write('Return value: {}\n'.format(res))
            return res
        return callf
    else:
        return func

@trace
def square(x):
    """Calculate the square of the given number."""
    return x * x

if __name__ == '__main__':
    print(square(3))
    print(square.__doc__)
    print(square.__name__)
    print('debug_log' in square.__globals__)
    print('sys' in square.__globals__)

输出
Calling function: square
Return value: 9
9
Calculate the square of the given number.
square
True
True

通过functools的wraps实现了在获取“square.name”信息时,输出的是square

二、引申:wraps装饰器分析

对代码要求较高,比较感兴趣的话,可以了解下

1、带参数的装饰器

def trace(log_level):
    def impl_f(func):
        print(log_level, 'Implementing function: "{}"'.format(func.__name__))
        return func
    return impl_f


@trace('[INFO]')
def print_msg(msg):
    print(msg)


@trace('[DEBUG]')
def assert_(expr):
    assert expr

print_msg('Hello, world!')

输出
[INFO] Implementing function: "print_msg"
[DEBUG] Implementing function: "assert_"
Hello, world!

装饰器理解

@trace('[INFO]')
def print_msg(msg): print(msg)

等价于

temp = trace('[INFO]')
def _print_msg(msg): print(msg)
print_msg = temp(_print_msg)

2、partial函数

# coding: utf-8
from functools import partial


def foo(x, y, z):
    print(locals())

foo(1, 2, 3)

foo_without_z = partial(foo, z = 100)
foo_without_z

foo_without_z is foo

foo_without_z(10, 20)

输出:
{'z': 3, 'y': 2, 'x': 1}
{'z': 100, 'y': 20, 'x': 10}

说明:
我们通过partial为foo提供参数z的值,得到了一个新的“函数对象”
foo_without_z和一般的函数对象有些差别。比如,foo_without_z没有name属性
文档字符串doc也和partial的文档字符串很相像

foo_without_z(10, 20)

等价于

foo(10, 20, z = 100)
上一篇 下一篇

猜你喜欢

热点阅读