前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【编程课堂】装饰器浅析

【编程课堂】装饰器浅析

作者头像
Crossin先生
发布2018-04-17 11:56:50
8620
发布2018-04-17 11:56:50
举报
文章被收录于专栏:Crossin的编程教室

Python 拥有丰富强大的功能和表达特性,其中之一便是装饰器,装饰器能够在不改变函数、方法、类本身的情况下丰富他们的功能。

比如,我们有一个函数 func ,我们希望在不改变函数的前提下记录函数运行的时间。 再比如,web 开发中,对于某一功能 vip_func ,只允许 VIP 用户使用,在不改变该函数本身的情况下,该如何做呢?

类似的例子还有很多,今天我们结合大量的例子来谈谈装饰器。对装饰器不太了解的同学,准备空闲的 30 分钟,打开编辑器,一起开始本周的学习之旅吧!

1、关于函数你应该知道

在正式介绍装饰器之前,很有必要了解一些函数的基本特性,这对理解装饰器很有帮助。

1.1 函数可以作为变量
代码语言:javascript
复制
def print_func(name):
    return 'hello,'+ name
func = print_func

print(func('world'))
#结果 : hello,world

从以上的例子可以看到,函数可以作为变量传递。

1.2 将函数传递给函数

既然函数可以作为变量,那就可以传递给另一个函数。

代码语言:javascript
复制
def prt_fun():
    return 'hello,world'

def call_func(func):
    return func()

print(call_func(prt_fun))
#结果 : hello,world
1.3 函数嵌套函数

先看一个简单的无参数的函数嵌套例子

代码语言:javascript
复制
def func_wrap():
    def prt_func():
        return 'hello,world'
    return prt_func

hlowld = func_wrap()
print(hlowld())
#结果 : hello,world

再来看一个将普通字符串作为参数的函数嵌套例子

代码语言:javascript
复制
def func_wrap():
    def prt_func(name):
        return 'hello,'+name   
    return prt_func

hlo = func_wrap()
print(hlo('crossin'))
#结果 : hello,crossin

最后,我们再来看将一个函数作为参数的函数嵌套例子,该嵌套函数的作用是在经过某函数处理的字符串两边添加 <p>...</p> 标签。

代码语言:javascript
复制
# 首先定义一个普通的函数
def print_text(name):
    return 'hello,'+ name
# 再定义一个嵌套函数,分别以函数和普通的字符串作为参数
def add_tag(func):
    def prt_func(name):
        return '<p>{0}</p>'.format(func(name))    
    return prt_func
    
# 将函数作为参数传递给 add_tag
hlo = add_tag(print_text)
# 将 'crossin' 作为参数传递给 hlo
print(hlo('crossin'))
# 结果 : <p>hello,crossin</p>

到这里,可能有些同学会有点懵了,没关系,请结合上一个例子和 1.2 节内容再理解一下,同时自己动手实现一个类似的函数。

没问题的同学接着往下看。

2、装饰器

本节正式进入装饰器的知识,装饰器的核心内容其实就是将函数作为参数传递给另一个函数。 装饰器的使用比较简单,如下图中的伪代码所示,decorator 为装饰器函数,func为被处理函数。

代码语言:javascript
复制
@decrator
def func():
    pass
2.1 无参数的装饰器

首先回到 1.3 节, 我们将此代码片段稍作修改,就是一个标准的装饰器实例

代码语言:javascript
复制
# 定义一个嵌套函数,分别以函数和普通的字符串作为参数
def add_tag(func):
    def prt_func(name):
        return '<p>{0}</p>'.format(func(name))    
    return prt_func
# 定义一个普通的函数,并调用装饰器

@add_tag
def print_text(name):
    return 'hello,'+ name

print(print_text('crossin'))
# 结果 : <p>hello,crossin</p>

是不是很神奇,仅仅调用一句 @add_tag 就轻松的将 hello,crossin 包裹了起来,实现的原理见 1.3 节解析,简单来讲就是将函数和字符串都作为参数传递给装饰器函数。

至此,你可以开开心心的将该装饰器使用在别的函数身上。

代码语言:javascript
复制
@add_tag
def func1(word):
    return 'arg is '+ word

print(func1('abc'))
# 结果 : <p>arg is abc</p>
2.2 带参数的装饰器

通过 2.1 节内容,我们对装饰器有了简单的理解,问题也随之而来,刚刚我们只能使用 <p>标签包裹,接下来,我们看看如何在不重新写其他装饰器的前提下,随心所欲的使用 <div><img>等标签包裹 文本。

代码语言:javascript
复制
# 定义装饰器函数
def add_tag(tagname):
    def decorator(func):
        def prt_func(name):
            return '<{0}>{1}</{0}>'.format(tagname,func(name)) 
        return prt_func    
    return decorator
       
@add_tag('div')
def print_text(name):
    return 'hello,'+name

print(print_text('crossin'))
# 结果 : <div>arg is abc</div>

这里,把原装饰器函数改为了 3 层嵌套,形式上虽然复杂了些,但原理上与之前的函数相同,实际运行中分别传入 div 字符串,print_text 函数地址, crossin 字符串,共同作用之后得到最终的结果。

2.3 __name__ 之惑

__name__可以获得函数、方法、类名,比如我们定义一个函数,然后获取其函数名

代码语言:javascript
复制
def func():
    pass
print(func.__name__)
# 结果 : func

但是,当我们去获取刚刚使用了装饰器的函数 print_text__name__

代码语言:javascript
复制
print(print_text.__name__)
# 结果 : prt_func

奇怪,为什么这里变为了装饰器内的函数名 prt_func,而不是 print_text,这是因为在装饰器中,prt_func 覆写了 print_text 函数的 __name____doc____modual__ 三个属性。

改回来也相当简单,使用Python 中的 functools.wraps 装饰器就可以了。

代码语言:javascript
复制
from functools import wrap
# 定义装饰器函数

def add_tag(tagname):
    def decorator(func):
        @wraps(func)
        def prt_func(name):
            return '<{0}>{1}</{0}>'.format(tagname,func(name))
        return prt_func
    return decorator

print(print_text.__name__)
# 结果 : print_text
3、小结

说了这么多,相信大家都看累了,来动动手吧。定义一个函数,添加一个装饰器输出该函数的运行时间。

同时,提供一些参考资料:

A guide to Python’s function decorators:

http://thecodeship.com/patterns/guide-to-python-function-decorators/

廖雪峰教程:

http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000

如何理解Python装饰器?:

https://www.zhihu.com/question/26930016

12步轻松搞定python装饰器:

http://python.jobbole.com/81683/

『码上行动』在线学习班正在开放中,详情回复 码上行动

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

本文分享自 Crossin的编程教室 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、关于函数你应该知道
    • 1.1 函数可以作为变量
      • 1.2 将函数传递给函数
        • 1.3 函数嵌套函数
        • 2、装饰器
          • 2.1 无参数的装饰器
            • 2.2 带参数的装饰器
              • 2.3 __name__ 之惑
              • 3、小结
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档