ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

metadata流失与带参装饰器

2021-07-20 17:35:55  阅读:180  来源: 互联网

标签:__ return name 流失 func multiply metadata 装饰 log


2个问题

  • 如何解决被装饰过的函数的metadata的流失的问题?
  • 带有参数的装饰器有什么用处?

如何解决被装饰过的函数的metadata的流失的问题?

# decorator
def log_func(func):
    def wrapper(*args, **kwargs):
        print('Function name: {}'format(func.__name__))
        return func(*args, **kwargs)
    return wrapper

# decorated function
@log_func
def multiply(x, y):
    'Multiply x and y'
    return x * y

multiply(1, 3)
# >> Function name: multiply
# >> 3

似乎看起来没问题,但是如果我们进一步检查被装饰过后的函数multiply各种attributes,我们就会发现问题:

# original function
def multiply(x, y):
    'Multiply x and y'
    return x * y

multiply.__name__
# >> 'multiply'
multiply.__doc__
# >> 'Multiply x and y'


# decorated function
@log_func
def multiply(x, y):
    'Multiply x and y'
    return x * y

multiply.__name__
# >> 'wrapper'
multiply.__doc__
# >> 

可以看到__doc___name__等函数的metadata,都在被装饰后,都丢失了。为了解决这个问题,我们可以在装饰器里,把这些metadata都复制到被装饰过后到函数里。

# original decorator
def log_func(func):
    def wrapper(*args, **kwargs):
        print('Function name: {}'format(func.__name__))
        return func(*args, **kwargs)
    return wrapper


# decorator that copies metatdata
def log_func(func):
    def wrapper(*args, **kwargs):
        print('Function name: {}'format(func.__name__))
        return func(*args, **kwargs)
    wrapper.__name__ = func.__name__ # copy name
    wrapper.__doc__ = func.__doc__ # copy doc
    return wrapper

在python的functools包里有提供一个装饰器@wraps就专门提供这个功能,所以我们的log_func装饰器可以重新定义为

 # decorator that copies metatdata with @functools.wraps
import functools

def log_func(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('Function name: {}'format(func.__name__))
        return func(*args, **kwargs)
    return wrapper


# decorate function with the above decorator
@log_func
def multiply(x, y):
    'Multiply x and y'
    return x * y

multiply.__name__
# >> 'multiply'
multiply.__doc__
# >> 'Multiply x and y'

带有参数的装饰器有什么用处?

什么是带参数的装饰器? 细心的朋友可能已经发现在上文里,我介绍的@functools.wraps在@语法中,有一个参数func@functools.wraps(func)。而常见的装饰器在@语法中是没有参数的,比如上文用到的@log_func 。类似@functools.wraps(func)就是带参数的装饰器;装饰器可以带多个参数@decorator(x, y)

看一个例子。

@log_msg_func("Function name: {name}")
def multiply(x, y):
    return x * y

在这个例子里,log_msg_func就是一个带参数的decorator。其实就等同于

log_func = log_msg_func("Function name: {name}")

@log_func
def multiply(x, y):
    return x * y

进一步等同于

def multiply(x, y):
    return x * y
multiply = log_msg_func("Function name: {name}")(multiply)

可见带参数的装饰器就一个返回值为一个装饰器的函数,其实就是一个“装饰器生成器“。这个例子里,log_msg_func的参数就是一个string,返回值就是一个装饰器。 我们看看其是怎么定义的:

def log_msg_func(msg):
    def log_func(func):
        def wrapper(*args, **kwargs):
            print(msg.format(name=func.__name__))
            return func(*args, **kwargs)
        return wrapper
    return log_func

从上面的定义里面可以看出,msg这个string参数为要生成的装饰器log_func提供了一个enclosing环境。有了这个log_msg_func”装饰器生成器“ ,我们可以轻松改变log里信息,进一步避免重复的代码。这就是带参数的装饰器的用处!

标签:__,return,name,流失,func,multiply,metadata,装饰,log
来源: https://www.cnblogs.com/soymilk2019/p/15035975.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有