前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python进阶之强大的装饰器 Decorators (三)

Python进阶之强大的装饰器 Decorators (三)

作者头像
用户4945346
发布2020-06-16 10:18:50
2680
发布2020-06-16 10:18:50
举报
文章被收录于专栏:pythonista的日常pythonista的日常

接下来要来谈谈 functools.wraps 的功用,虽然使用装饰器可以大大的减少重复的 code,但是他有一个缺点,就是你会发现 f1 function 中的 name 和 doc 结果会怪怪的,以下的例子。

代码语言:javascript
复制
def my_logging(func):
    def wrapper(*args, **kwargs):
        """my wrapper"""
        print('logging - {} is running'.format(func.__name__))
        func(*args, **kwargs)

    return wrapper


@my_logging
def f1(*args, **kwargs):
    """f1 function"""
    print("f1")

    for thing in args:
        print('hello {}'.format(thing))

    for name, value in kwargs.items():
        print('{0} = {1}'.format(name, value))


f1('twtrubiks', apple='fruit', cabbage='vegetable')
print('f1.__name__', f1.__name__)  # output -> 'wrapper'
print('f1.__doc__', f1.__doc__)  # output -> 'my wrapper'

将 f1.__name__ 以及 f1.__doc__ 印出来,竟然出现了 wrapper 的东西,( 也就是说,f1 被 wrapper 取代了,所以才显示 wrapper 的信息 ),所以为了解决这个问题,可以使用 functools.wraps 这个装饰器,将原本函数里面的信息复制到 func 之中,这样就会让 func 也有原函数 f1 的信息,方法如下。

代码语言:javascript
复制
from functools import wraps


def my_logging(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """my wrapper"""
        print('logging - {} is running'.format(func.__name__))
        func(*args, **kwargs)

    return wrapper


@my_logging
def f1(*args, **kwargs):
    """f1 function"""
    print("f1")

    for thing in args:
        print('hello {}'.format(thing))

    for name, value in kwargs.items():
        print('{0} = {1}'.format(name, value))


f1('twtrubiks', apple='fruit', cabbage='vegetable')
print('f1.__name__', f1.__name__)  # output -> 'f1'
print('f1.__doc__', f1.__doc__)  # output -> 'f1 function'

这样子就可以正确的显示 f1 的资讯了。装饰器除了装饰 function 之外,也可以装饰 class,class decorator 主要是依赖 __call__ 的方法。

代码语言:javascript
复制
class MyDecorator:
    def __init__(self, param):
        self.__param = param

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print('do something before calling function {}'.format(func.__name__))
            print('self.__param', self.__param)
            func(*args, **kwargs)
            print('do something after calling function {}'.format(func.__name__))

        return wrapper


@MyDecorator('level')
def f1(*args, **kwargs):
    print('f1')
    for thing in args:
        print('hello {}'.format(thing))

    for name, value in kwargs.items():
        print('{0} = {1}'.format(name, value))


f1('twtrubiks', apple='fruit', cabbage='vegetable')

看起来稍微比较复杂,但其实就是 closures 闭包的概念。

装饰器还有一个神奇的功能,就是用装饰器来注册函数,先来看一个 flask 框架中常常用来注册 url 的方法,相信如果写过 flask 的人,一定对下面这段 code 不陌生。

代码语言:javascript
复制
@app.route("/")
def index():
    pass

但是你有想过,他是怎麽办到的吗❓其实就是利用了我刚刚说的另外一个特性,用装饰器来注册函数。不太懂意思,没关系,来看一个例子。

代码语言:javascript
复制
registry = dict()

def route(rule):
    def decorator(f):
        registry[rule] = f # <1>
        return f

    return decorator


@route('/')
def index():
    print('hello')
    return 'hello'

index()
print('registry:', registry) # <2>

重点在 <1> 的部分,我们只是将 rule 以及 f 注册到 registry,然后再 return 原本的 function ( 没经过任何加工,只单纯注册 url ),如果你观察 <2> 的输出,你也会发现 registry 里面有 url 的 mapping。所以装饰器除了装饰 ( 扩充 ) 原本的功能之外,还有注册函数的功用。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 pythonista的日常 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档