前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >开源图书《Python完全自学教程》7.3.4装饰器

开源图书《Python完全自学教程》7.3.4装饰器

作者头像
老齐
发布2022-07-06 16:04:56
3510
发布2022-07-06 16:04:56
举报
文章被收录于专栏:老齐教室

7.3.4 装饰器

在理解了嵌套函数的基础上,请读者耐心阅读以下代码。

代码语言:javascript
复制
#coding:utf-8
'''
filename: decorate.py
'''

def book(name):
    return f"the name of my book is {name}"

def p_decorate(func):
    def wrapper(name):
        return f"<p>{func(name)}</p>"
    return wrapper

if __name__ == "__main__":
    my_book = p_decorate(book)    # (17)
    result = my_book("PYTHON")    # (18)
    print(result)

先看执行结果,再解释程序。

代码语言:javascript
复制
% python decorate.py 
<p>the name of my book is PYTHON</p>

函数 book() 是一个普通的函数,函数 p_decorate() 是嵌套函数,外层函数的参数 func 所引用的对象必须可执行,并且是 func(name) 形式,正好 book() 函数可以满足(其它满足要求的函数亦可,这里仅以 book() 为例)。根据对嵌套函数的理解,注释(17)得到了闭包,注释(18)执行写在 p_decorate() 函数里面的 wrapper() 函数对象。最后打印返回值。

注意观察注释(18)执行后的返回值,已经与 book() 函数中的返值不同。由此结果可以说,函数 book() 的行为(例如返回对象)经过注释(17)后发生了改变,即被函数 p_decorate() 改变了——这种改变并不是重写 book() 函数,而是基于该函数原内容的变化。就如同一个女子或是“对镜贴花黄”或是“云鬓蓬松眉黛浅”,皆因化妆不同而看起来有不同的状态。于是可将函数 p_decorate() 类比于“花黄”,也给它取了一个形象的名字,称它是 book() 函数的装饰器(Decorator)。根据注释(17)可知,装饰器包裹一个函数之后,即改变其行为。

英国计算机科学家彼得·约翰·兰丁在研究闭包的同时(参阅7.3.2节),发明了语法糖(Syntactic Sugar)这个术语,意指编程语言中的一种语法,该语法只是让程序更简洁,可读性更强,并不影响功能。Python 语言中有以 @symbol 的形式实现装饰器语法糖。

decorate.py 中的程序修订如下,注意其中语法糖的运用。

代码语言:javascript
复制
#coding:utf-8
'''
filename: decorate.py
'''

def p_decorate(func):
    def wrapper(name):
        return f"<p>{func(name)}</p>"
    return wrapper

@p_decorate                         # (19)
def book(name):
    return f"the name of my book is {name}"

if __name__ == "__main__":
    # my_book = p_decorate(book)
    # result = my_book("PYTHON")
    result = book("Learn Python")    # (20)
    print(result)

函数 p_decorate() 必须在 book() 之前,然后在函数 book() 上面增加了注释(19),即装饰器语法糖,再用注释(20)调用 book() 函数,其效果与修改前的程序等同。

代码语言:javascript
复制
% python decorate.py
<p>the name of my book is Learn Python</p>

甚至于可以编写多个装饰器,以语法糖的形式,将其作用于一个函数,例如:

代码语言:javascript
复制
#coding:utf-8
'''
filename: moresugar.py
'''
def p_decorate(func):
    def wrapper(name):
        return f"<p>{func(name)}</p>"
    return wrapper

def div_decorate(func):
    def wrapper(name):
        return f"<div>{func(name)}</div>"
    return wrapper

@div_decorate
@p_decorate
def book(name):
    return f"the name of my book is {name}"

if __name__ == "__main__":
    result = book("PYTHON")
    print(result)

执行结果是:

代码语言:javascript
复制
% python moresugar.py 
<div><p>the name of my book is PYTHON</p></div>

学习了装饰器和语法糖的基本用法之后,就要看看它们在实践中如何使用。下面就编写一个用于测量函数执行时间的装饰器。

代码语言:javascript
复制
#coding:utf-8
'''
filename: timing.py
'''
import time

def timing_func(func):
    def wrapper():
        start = time.time()
        func()
        stop = time.time()
        return (stop - start)
    return wrapper

@timing_func
def test_list_append():
    lst = []
    for i in range(0, 100000):
        lst.append(i)

@timing_func
def test_list_compre():
    [i for i in range(0, 100000)]

if __name__ == "__main__":
    a = test_list_append()
    c = test_list_compre()
    print("test list append time:", a)
    print("test list comprehension time:", c)
    print("append/compre:", round(a/c, 3))

程序执行结果(不同性能的计算机,以下结果的数值可能有所不同):

代码语言:javascript
复制
% python timing.py 
test list append time: 0.012425422668457031
test list comprehension time: 0.004575967788696289
append/compre: 2.715

将装饰器语法糖分别作用于两个函数,测量它们的执行时间。不仅程序显得简洁,且可读性强。另外,执行结果再次说明列表解析是必须要掌握的(参阅第6章6.4节)。

虽然我们是在嵌套函数的基础上引入了装饰器,但装饰器并不完全等同于嵌套函数,普通函数也能够作为装饰器,比如:

代码语言:javascript
复制
#coding:utf-8
'''
filename: decosimplyfunc.py
'''

import random
PLUGINS = dict()     # (21)

def register(func):
    PLUGINS[func.__name__] = func
    return func

@register
def say_hello(name):
    return f"Hello {name}"

@register
def be_awesome(name):
    return f"Yo {name}, together we are the awesomest!"

def randomly_greet(name):
    greeter, greeter_func = random.choice(list(PLUGINS.items()))
    print(f"Using {greeter!r}")
    return greeter_func(name)

print(PLUGINS)
calling_func = randomly_greet('laoqi')
print(calling_func)

先看执行结果,再解释代码:

代码语言:javascript
复制
% python decosimplyfunc.py
{'say_hello': <function say_hello at 0x7ff4b2d52700>, 'be_awesome': <function be_awesome at 0x7ff4b2d52790>}
Using 'say_hello'
Hello laoqi

# 再次执行
% python decosimplyfunc.py
{'say_hello': <function say_hello at 0x7fb36d652700>, 'be_awesome': <function be_awesome at 0x7fb36d652790>}
Using 'be_awesome'
Yo laoqi, together we are the awesomest!

注释(21)中以 PLUGINS 命名变量,通常用它表示“全局变量”,即在整个程序中都使用的变量。这也是约定俗成,并非强制。

以函数 register() 为装饰器,用语法糖的形式“装饰”了函数 say_hello()be_awesom() ,则 PLUGINS 引用的字典中含有这两个函数对象(请观察执行结果)。函数randomly_greet() 从字典中随机选出一个函数,并执行。

这里仅介绍了装饰器的初步知识,读者在理解本节内容基础上,可以于此后对相关应用进行深入研究。

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

本文分享自 老齐教室 微信公众号,前往查看

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

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

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