57、Python之函数高级:装饰器,开闭原则下增强函数功能

引言

今天的文章中,我们继续聊下Python中关于函数高级用法中的“装饰器”。

装饰器其实我们之前已经用到过,比如@property。而且在上一篇文章中,关于功能迭代中应用高阶函数的特性,本质上也是在使用装饰器。

由于装饰器模式可以算是对开闭原则的一个最佳实践,所以,这篇文章中,我们会首先回顾一下,之前介绍过的开闭原则的内容,然后再引入装饰器模式的部分,最后看下Python关于装饰器的语法糖的本质。

本文主要内容有:

1、再看“开闭原则”

2、装饰器模式

3、Python中的装饰器语法糖

再看“开闭原则”

所谓的“开闭原则(Open/Closed Principle,OCP)”,是软件设计中的一个重要原则,是面向对象设计的五大基本原则之一,通常用于指导模块化和可扩展的设计及开发。

开闭原则,简单说来就是两点:

1、对扩展开放(Open for extension):这是“开”的含义。业务场景及用户需求是在不断发展、变化中的,所以,软件系统要能进行扩展,以满足新的需求。

2、对修改关闭(Closed for modification):软件模块一旦开发完成,就应该尽量不要修改其源代码了(虽然很多时候都做不到,但是很多血淋淋的教训告诉我们,应该努力向着这个目标前进)。

之所以要尽量遵循开闭原则,是因为该原则能够提高软件系统的可扩展性和可维护性。通过遵循这一原则,开发者可以在不修改现有代码的情况下,增加新的功能或者改变系统行为,从而降低引入新的错误的风险,增强了系统的稳定性和可靠性。

此外,在设计开发过程中,通过遵循开闭原则,还可以提升设计的质量,促进代码的复用。在测试过程中,由于不需要修改现有的代码,还可以减少回归测试的需求,从而降低了测试、维护成本。

装饰器模式

装饰器模式(Decorator Pattern)是面向对象设计模式中一个比较常用的结构型设计模式,旨在践行开闭原则,通过动态地给对象添加新的行为,而不改变其原有代码。

装饰器模式通常通过创建一个装饰器类,这个类包装了原始类,并通过组合和委托的方式实现新功能。

装饰器模式的主要特点有:

1、动态扩展:开闭原则的基本要求,可以在运行时动态为对象添加新的行为。

2、灵活性高:可以根据需要组合多个装饰器,从而实现复杂的功能扩展。

3、遵循单一职责原则:通常来说,每个装饰器类都只负责一个特定的功能扩展。

4、透明性:装饰器类对客户端透明,客户端无需关注对象是否被装饰。

需要说明的是,面向对象设计模式中的装饰器,更多的强调对对象的动态增强,而Python中的装饰器,很多时候虽然是基于高阶函数(闭包)的特性,但是由于“Python中一切皆对象”,所以,也可以理解为是对对象的动态增强。而且,不同于Java等编程语言,Python中可以通过闭包实现装饰器模式,从而使得装饰器模式的应用变得更加灵活、简便。

Python中的装饰器语法糖

接下来,还是通过代码看一下如何在不改变函数代码的同时,动态扩充函数功能,而且对客户端(函数调用端)完全透明。然后,再总结Python中装饰器的简单使用方法及语法糖的部分。

直接看代码:

有一个模块名为m1:

def read(user):
    print(f"用户[{user}]查看系统相关信息")


def write(user):
    print(f"用户[{user}]修改系统相关信息")

入口文件,调用m1中的函数功能:

import m1

if __name__ == '__main__':
    user = '张三'
    m1.read(user)
    m1.write(user)

执行结果:

a5c500ea22cddfe9b254803f95b77104.jpeg

后来,系统用户多了,发现谁都能查看、修改有点风险。需要增加登录验证功能,只有登录用户才允许使用该系统。

基于开闭原则的理念,我们使用装饰器来实现不修改函数源代码,进行函数功能的动态扩充:

仅修改m1.py的代码,修改为:


def read(user):
    print(f"用户[{user}]查看系统相关信息")


def write(user):
    print(f"用户[{user}]修改系统相关信息")


# 扩展1:新增登录功能
def login(user):
    print(f"用户[{user}]登录成功")


# 扩展2:装饰器:对传入的函数进行登录功能的动态添加
def login_wrap(func):
    def inner(user):
        login(user)
        func(user)

    return inner


# 扩展3:对原有reda/write进行装饰,确保对用户调用端透明
read = login_wrap(read)
write = login_wrap(write)

可以看到,read()和write()函数的源代码没有进行任何修改,我们只是增加了动态扩展的装饰器,及对需要进行扩展的函数进行了装饰。

执行原入口文件的结果:

30f6667e3151cb55a4629a0d6a9f4c9d.jpeg

简单总结一下,应用装饰器进行功能动态扩展的方法:

1、定义新功能、特性的函数(也可以是类等)。

2、定义一个装饰器函数,就是一个嵌套函数,这个函数只有一个参数,就是传入要扩展的历史函数。返回值是一个新的函数,这个函数实现对历史函数调用的委托机制,在调用历史函数的同时,同时调用新特性对应的函数。

3、实现调用端的透明、无感知,对需要进行动态扩展的函数进行装饰,然后将装饰器函数的返回值,与原函数名进行绑定,即:原函数名 = 装饰器(原函数名)。

为了更加偷懒,Python还提供了装饰器语法糖,让装饰器的实现进一步简化。也是就@property同样的用法。

我们修改一下,改用@login_wrap的方式,需要稍微调整一下代码的顺序:

# 扩展1:新增登录功能
def login(user):
    print(f"用户[{user}]登录成功")


# 扩展2:装饰器:对传入的函数进行登录功能的动态添加
def login_wrap(func):
    def inner(user):
        login(user)
        func(user)

    return inner


@login_wrap
def read(user):
    print(f"用户[{user}]查看系统相关信息")


@login_wrap
def write(user):
    print(f"用户[{user}]修改系统相关信息")

可以看到,我们无需手动调用login_wrap()来进行装饰了,只需要在需要装饰的函数定义之前添加上@login_wrap即可。

虽然写法不一样,但是我们需要知道,@login_wrap本质上,就是Python解释器帮我们执行了装饰器函数调用并赋值给原函数名的那行代码。

总结

本文首先回归了开闭原则的相关内容,然后介绍了开闭原则的一个最佳实践——装饰器模式,最后简单介绍了Python中装饰器的用法。

需要说明的是,本文只是简单介绍了一下装饰器,装饰器中还有一些稍微复杂的内容,我们在后面的文章再一一介绍。

感谢您的拨冗阅读,希望对您学习Python有所帮助!

fecf3e95fa99ba711fa9f65b5841814e.jpeg

  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南宫理的日知录

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值