【编程课堂】装饰器浅析

简介: 比如,我们有一个函数 func ,我们希望在不改变函数的前提下记录函数运行的时间。

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/


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


近期文章推荐阅读:

喏,你们要的 PyCharm 快速上手指南

给伸手党的福利:Python 新手引导

只学2个月编程能写出什么代码?他们表示:You can you code!

如何用100行Python代码做出魔性声控游戏“八分音符酱”

数据分析:当赵雷唱民谣时他唱些什么?

一行代码扫出“敬业福”

我扒了杜蕾斯的微博

Python 爬虫爬取美剧网站

今天,你抢到票了吗?

爆款游戏《贪吃蛇大作战》的 Python 实现

相关文章
|
Java
Java面向对象编程(5)
Java面向对象编程(5)
41 0
|
存储 缓存 Java
Java面向对象编程(31)
Java面向对象编程(31)
50 0
|
2月前
|
设计模式 开发框架 测试技术
探索Python编程中的装饰器
【9月更文挑战第36天】在Python的世界里,装饰器是一个既神秘又强大的工具。它们不仅能够增强函数的功能,还能让代码更加简洁和模块化。本文将通过实例介绍装饰器的基本概念、使用方法以及如何自定义装饰器,旨在帮助读者掌握这一实用技术,从而提升代码的可读性和复用性。
|
3月前
|
Python
探索Python编程中的装饰器魔法
【9月更文挑战第26天】在Python的世界里,装饰器就像是一把瑞士军刀,小巧而功能强大。它们让代码更简洁、可维护性更强。本文将通过实际示例,带你领略装饰器的魔力,从基础到进阶,一步步揭开它的神秘面纱。
21 2
|
4月前
|
设计模式 Python
深入浅出:Python编程中的装饰器
【8月更文挑战第29天】装饰器,在Python中是改变函数或类行为的一种高级工具。它们允许我们在不修改原始代码的情况下,增加额外的功能。本文将通过浅显易懂的语言和实例,带你了解装饰器的概念、用法及其背后的原理,让你能够轻松掌握这一强大的工具。
|
5月前
|
程序员 Python
从零到一,彻底掌握Python闭包与装饰器的精髓,成为编程界的隐藏Boss
【7月更文挑战第7天】探索Python编程的两大基石:闭包与装饰器。闭包是内部函数记住外部作用域的变量,如`make_multiplier_of`返回的`multiplier`,它保持对`n`的引用。装饰器则是函数工厂,接收函数并返回新函数,如`my_decorator`,它在不改变原函数代码的情况下添加日志功能。掌握这些,让代码更优雅,效率更高,助你成为编程高手。
35 3
|
5月前
|
Python
Python黑魔法揭秘:闭包与装饰器的高级玩法,让你代码飞起来
【7月更文挑战第7天】Python的闭包和装饰器是提升代码效率的神器。闭包是能记住外部作用域变量的内部函数,常用于动态函数创建。示例中,`make_multiplier_of`返回一个保留`n`值的闭包。装饰器则是一个接收函数并返回新函数的函数,用于在不修改原函数情况下添加功能,如日志或性能追踪。`@my_decorator`装饰的`say_hello`函数在执行时会自动加上额外操作。掌握这两者,能让Python代码更优雅、强大。**
36 1
|
7月前
|
存储 API Swift
【Swift开发专栏】Swift函数与闭包的实战应用
【4月更文挑战第30天】本文介绍了 Swift 中函数和闭包的实战应用。首先,函数的基本使用包括定义、参数与返回值、函数类型以及高级技巧如嵌套函数。接着,讨论了闭包的语法,包括无名函数、作为函数参数、简写形式和尾随闭包。最后,展示了函数和闭包在实战中的应用,如排序过滤集合和处理异步任务的回调。
54 0
|
7月前
|
Python
Python编程中的装饰器应用探究
【2月更文挑战第8天】装饰器是Python编程中一个重要且强大的工具,它能够简洁地实现函数的包装和扩展,提高代码的复用性和可读性。本文将深入探讨装饰器在Python编程中的应用,结合实际例子详细讲解装饰器的定义、使用方法及常见应用场景,帮助读者更好地理解和运用装饰器这一高级特性。
|
7月前
|
Python
Python编程中的装饰器应用探索
【2月更文挑战第6天】本文将深入探讨Python编程中装饰器的应用,介绍装饰器的定义、作用以及实际应用场景,并结合示例代码详细阐释装饰器在函数、类等方面的灵活运用,帮助读者更好地理解和使用装饰器提升代码的可复用性和可维护性。