【编程课堂】装饰器浅析

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

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

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

1、关于函数你应该知道

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

1.1 函数可以作为变量

def print_func(name):    return 'hello,'+ name
func = print_func print(func('world'))
#结果 : hello,world

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

1.2 将函数传递给函数

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

def prt_fun():    return 'hello,world'

def call_func(func):    return func() print(call_func(prt_fun))
#结果 : hello,world

1.3 函数嵌套函数

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

def func_wrap():    def prt_func():        return 'hello,world'    return prt_func hlowld = func_wrap() print(hlowld())
#结果 : hello,world

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

def func_wrap():    def prt_func(name):        return 'hello,'+name  
   return prt_func hlo = func_wrap() print(hlo('crossin'))
#结果 : hello,crossin

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

# 首先定义一个普通的函数
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为被处理函数。

@decrator
def func():    pass

2.1 无参数的装饰器

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

# 定义一个嵌套函数,分别以函数和普通的字符串作为参数
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 节解析,简单来讲就是将函数和字符串都作为参数传递给装饰器函数。

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

@add_tag
def func1(word):    return 'arg is '+ word print(func1('abc'))
# 结果 : <p>arg is abc</p>

2.2 带参数的装饰器

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

# 定义装饰器函数
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__可以获得函数、方法、类名,比如我们定义一个函数,然后获取其函数名

def func():    pass
print(func.__name__)
# 结果 : func

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

print(print_text.__name__)
# 结果 : prt_func

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

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

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/

(0)

相关推荐

  • python笔记35-装饰器

    前言 python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象. 很多python初学者学到面向对象类和方法是一道大坎,那么p ...

  • P百科:如何理解 Python 中的装饰器

    昨天 [P百科]:Python 大白话百科全书   每周,痴海会用大白话,讲解一个 Python 中那些难以理解的知识点.   希望通过这样的白话讲解,可以让大家更好的学会 Python.   今天这 ...

  • Python学习—装饰器

    学习Python已经有一段时间了,陆续学了一些基础部分,但是理解的不是很深刻,每过一段时间就会忘记,所以不得不写一些博客进行记录,加深自己的理解.这两个星期一直在研究装饰器,开始觉得很简单,但是只知其 ...

  • 一文看懂Python系列之装饰器(decorator)(工作面试必读)

    Python的装饰器(decorator)可以说是Python的一个神器,它可以在不改变一个函数代码和调用方式的情况下给函数添加新的功能.Python的装饰器同时也是Python学习从入门到精通过程中 ...

  • python到底还有哪些函数我们还没见过?原来有这么多实用的函数

    本文将将主要介绍如下内容 函数介绍 1.1 函数的定义 所谓的函数,其实就是具有特定功能的一段代码块,都可以独立的运行 . 函数有5部分组成,分别是定义函数的关键字,函数的名字,圆括号(存放函数的参数 ...

  • Python|装饰器

    一对象的概念python的所有内容都可以作为对象,这意味着这些内容可以作为参数作用于其他的"对象",这不难理解,就像在函数中,可以把另一个函数作为参数,甚至是类作为参数,,因此经常 ...

  • Python 中的函数装饰器和闭包

    函数装饰器可以被用于增强方法的某些行为,如果想自己实现装饰器,则必须了解闭包的概念. 装饰器的基本概念 装饰器是一个可调用对象,它的参数是另一个函数,称为被装饰函数.装饰器可以修改这个函数再将其返回, ...

  • [PHP小课堂]PHP设计模式之装饰器模式

    [PHP小课堂]PHP设计模式之装饰器模式 关注公众号:[硬核项目经理]获取最新文章 添加微信/QQ好友:[DarkMatterZyCoder/149844827]免费得PHP.项目管理学习资料

  • 浅析Python装饰器

    浅析Python装饰器

  • Selenium2+python自动化55-unittest之装饰器(@classmethod)

    前言 前面讲到unittest里面setUp可以在每次执行用例前执行,这样有效的减少了代码量,但是有个弊端,比如打开浏览器操作,每次执行用例时候都会重新打开,这样就会浪费很多时间. 于是就想是不是可以 ...

  • 设计模式-装饰器模式

    装饰器模式 定义 装饰器模式也叫包装模式 在不改变原有对象的基础上,把功能附加到对象上,提供了比继承更有弹性的替代方案 能够扩展原有对象的功能 属于结构型模式 生活中的例子 买煎饼 我们煎饼可以加鸡蛋 ...

  • PHP设计模式之装饰器模式

    PHP设计模式之装饰器模式 工厂模式告一段落,我们来研究其他一些模式.不知道各位大佬有没有尝试过女装?据说女装大佬程序员很多哟.其实,今天的装饰器模式就和化妆这件事很像.相信如果有程序媛MM在的话,马 ...

  • 3年工作必备 装饰器模式

    回复"000"获取大量电子书 大家好,我是老田,从今天开始,本公众号每周给大家送福利,送什么呢?肯定是技术书啦,没那么多花里胡哨的,参与方式见文末. 好啦,进入我们的主题,今天我给 ...

  • 神奇的Python property装饰器:1行代码让Python方法秒变属性

    神奇的Python property装饰器:1行代码让Python方法秒变属性

  • 第20天:Python 之装饰器

    第20天:Python 之装饰器