注册 登录
Python基础教程

第一章: 环境搭建,安装Python

第二章: 挑选一款趁手的IDE

第三章: 计算机基础知识

第四章: 命令行基础知识

第五章: 从全局把握Python

第六章: Python语言基础

第七章: Python流程控制

第八章: Python数据类型与运算

第九章: Python字符串类型

第十章: Python列表类型

第十一章: Python元祖类型

第十二章: Python字典类型

第十三章: Python集合类型

第十四章: Python函数处理

第十五章: Python文件处理

第十六章: Python面向对象

第十七章: Python异常处理

第十八章: Python模块处理

第十九章: Python项目实战

首页 > Python基础教程 > 第十四章: Python函数处理 > 14.5节:理解函数装饰器

14.5节:理解函数装饰器

薯条老师 2020-05-22 07:57:42 208351 3

编辑 收藏

广州番禺Python, Java小班周末班培训

薯条老师的线下Python,Java小班周末班已经开课了,培训的课程有Python爬虫,Python后端开发,Python办公自动化,Python大数据分析,Java后端开发。授课详情请点击:http://chipscoco.com/?cate=6

14.5.1 理解函数装饰器

在进入正题前,先看一段有关"装饰"的词语解释,以下内容引自百度百科:
装饰,《辞源》解释为“装者,藏也,饰者,物既成加以文采也。”指的是对器物表面添加纹饰、色彩以达到美化的目的。
"装饰"本身就包含功能扩展的意思,例如对器物进行着色,即为色彩的扩展,对器物添加纹饰,即为纹饰的扩展。我们平时中的读书、健身、学习,亦何尝不是一种扩展,通过诸多途径来提升我们的内里与外在。

image.png

以此来进行类比,则很容易理解编程语言中的装饰器也是用来进行功能上的扩展。在面向对象中,装饰器是一种软件设计模式,可以对已有的对象进行功能上的扩展,而无需改变其结构。

在第十六章的课程中,会对面向对象编程进行详细的讲解。
那么,如何使用装饰器来进行功能上的扩展?我们得先学习Python中的闭包函数。

14.5.2 理解闭包函数

闭包函数,简单地理解,就是函数中定义的一个内部函数,该内部函数可以访问外部函数作用域中的参数,变量。
代码实例:
# __desc__ = 定义一个闭包函数
 
def login():
    """    实现用户的登录功能    """
    # 键名表示用户名,键值表示登录状态,0为未登录,1为登录 
 
    __USER_STATUS = {
        "green": 0,
    }
 
    # check就是一个闭包函数,访问了外部函数中的变量__USER_STATUS 
    def check(user_name="guest"):
        """  用户登录时,对用户的登录态进行检查  """
        if __USER_STATUS.get(user_name, 0) == 0:
           print("username incorrect or not login")
 
return check
代码讲解:
在上文代码中,定义了一个login函数,在login函数内部又定义了一个check函数,用来对用户的登录状态进行检查。login函数即为check函数的外部函数,在check函数内部访问了login函数中的_USER_INFO变量。像check这样的函数,即为闭包函数。

 

14.5.3 使用闭包进行功能扩展

假设我们在web开发中定义了一个欢迎页面,一开始不需要登录即可进入欢迎页。定义一个welcome函数,用来模拟欢迎页面的输出:
#__desc__ = 定义welcome函数,用来输出页面的欢迎信息
 
def welcome(user_name="guest"):
    print("welcome {}".format(user_name))
随着项目的不断迭代,现在需要用户登录以后,才弹出欢迎页。此时如何为welcome函数进行扩展?
有两种扩展方案:
(1) 直接对welcome函数进行修改,在welcome函数内部对用户的登录态进行检查
(2) 使闭包函数与welcome函数共享同样的参数,这样可以在闭包函数中对用户的登录态进行检查,如果用户已登录,再执行welcome函数。
采用第一种方案会改变welcome函数的代码逻辑,采用第二种方案,是将代码的扩展逻辑转移到闭包函数中,现在请读者回顾装饰器模式的定义:对已有的对象进行功能上的扩展,而无需改变其结构。采用的第二种方案正是所谓的装饰器模式,在Python的装饰器模式中,其核心在于利用闭包函数来对被装饰的对象,进行功能的扩展。

现在通过第二种方案,来对用户的登录态进行检查,Python中的函数可以接受任意类型的参数,我们可以直接将welcome函数作为参数传递给login函数。

代码实例:
# __desc__ = 通过闭包来对welcome函数进行扩展
 
def login(func):
    """  实现用户的登录功能  """
    # 键名表示用户名,键值表示登录状态,0为未登录,1为登录 
 
    __USER_STATUS = {
        "green": 0,
    }
 
    def check(user_name="guest"):
        """  用户登录时,对用户的账号和密码进行验证  """
        if __USER_STATUS.get(user_name, 0) == 0:
            print("username incorrect or not login")
        else:
            func(user_name)
    return check
 

def welcome(user_name="guest"):
    print("welcome {}".format(user_name))
 
# 执行login函数,将welcome函数作为参数传递给login函数
decorated_welcome = login(welcome)
 
# login返回的函数即为被装饰过后的welcome函数
# 再执行被装饰过的welcome函数
decorated_welcome("green")

14.5.4 更优雅的做法:装饰器语法糖

在14.5.3节中,我们通过闭包函数对其它函数的功能进行了扩展,但在使用上不够直观和自然:
(1) 需要将被装饰的函数作为参数传递给装饰器
(2) 需要再执行返回的闭包函数
Python中提供了语法糖,在函数头前面加上一行@decortator的修饰符,可以对当前函数进行装饰,decortator表示具体的装饰器名。在上文的代码中,login函数就是一种装饰器,现在使用@符号来对welcome函数进行装饰。
# __desc__ = 使用@符号对welcome函数进行装饰
 
def login(func):
    """    实现用户的登录功能    """
    # 键名表示用户名,键值表示登录状态,0为未登录,1为登录 
 
    __USER_STATUS = {
        "green": 0,
    }
 
    def check(user_name="guest"):
        """  用户登录时,对用户的登录态进行检查 """
        if __USER_STATUS.get(user_name, 0) == 0:
            print("username incorrect or not login")
        else:
            func(user_name)
    return check
 
@login
def welcome(user_name="guest"):
    print("welcome {}".format(user_name))
对welcome函数使用@login进行装饰以后,Python会自动将wecome函数作为参数传递给login函数, 并执行返回的闭包函数,这是Python装饰器的核心逻辑所在。装饰器中的闭包函数参数须与被装饰对象的参数一致,在不确定被装饰对象的参数时,可以使用可变参数:*args, **kwargs。使用可变参数的装饰器结构:
def decorator(func):
    def closure(*args, **kwargs):
        func(*args, **kwargs)
    return closure
使用装饰器语法糖时,装饰器也可以携带参数,通常需要再嵌套一层闭包函数,在装饰器的最外层函数定义装饰器的参数,在第二层传递被装饰的对象:
# __desc__ = 在最外层定义装饰器的参数
 
def decorator(*args, **kwargs):
    # 在第二层传递被装饰的对象f
    def closure_outer(func):
        def closure_inner(*args, **kwargs):
            # 在第三层中执行被装饰的对象
            func(*args, **kwargs)
        return closure_inner
    return closure_outer
现在继续对welcome函数进行扩展,当用户未登录时,跳转到登录页面。
代码实例:
# __desc__ = 继续对welcome函数进行扩展
 
def redirect(url):
    """ 定义redirect函数来模拟页面跳转 """
    print("redirect to {}".format(url))
 
 
def login(url):
    """    实现用户的登录功能    """
    # 键名表示用户名,键值表示登录状态,0为未登录,1为登录 
 
    __USER_STATUS = {
    "green": 0,
    }
 
    def __login(func):
        def check(user_name="guest"):
            """  用户登录时,对用户的登录态进行检查 """
                if __USER_STATUS.get(user_name, 0) == 0:
                    redirect(url)
 
                else:
                    func(user_name)
        return check
    return __login
 
@login("/login/")
def welcome(user_name="guest"):
    print("welcome {}".format(user_name))
在Python中,使用函数对象定义的装饰器,被称为函数装饰器。使用类类型定义的装饰器,被称为类装饰器。本篇内容着重讲了函数装饰器,对于类装饰器,也是一样的原理。
在后面的课程中,薯条老师会详细地讲解Python中的类装饰器。
装饰器的核心是对已有的对象进行功能扩展,而无需改变其结构。我们在实际开发中,可通过装饰器为可执行对象扩展缓存,日志输出等功能。

14.5.5 知识要点

(1) 装饰器也是用来进行功能上的扩展。在面向对象中,装饰器是一种软件设计模式,可以对已有的对象进行功能上的扩展,而无需改变其结构。
(2) 闭包函数,简单地理解,就是函数中定义的一个内部函数,该内部函数可以访问外部函数作用域中的参数,变量。

14.5.6 高薪就业班


已有3位薯条发表了看法:

  • 访客

    访客  评论于 2021-01-15 20:15:53  回复

    使用优雅的语法糖之后 如何优雅的调用呢

  • 访客

    访客  评论于 2021-01-15 20:16:28  回复

    使用优雅的语法糖之后 如何优雅的调用呢

欢迎 发表评论:

请登录

忘记密码我要注册

注册账号

已有账号?请登录