今天的文章中,我们继续聊下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:
入口文件,调用m1中的函数功能:
执行结果:
后来,系统用户多了,发现谁都能查看、修改有点风险。需要增加登录验证功能,只有登录用户才允许使用该系统。
基于开闭原则的理念,我们使用装饰器来实现不修改函数源代码,进行函数功能的动态扩充:
仅修改m1.py的代码,修改为:
可以看到,read()和write()函数的源代码没有进行任何修改,我们只是增加了动态扩展的装饰器,及对需要进行扩展的函数进行了装饰。
执行原入口文件的结果:
简单总结一下,应用装饰器进行功能动态扩展的方法:
1、定义新功能、特性的函数(也可以是类等)。
2、定义一个装饰器函数,就是一个嵌套函数,这个函数只有一个参数,就是传入要扩展的历史函数。返回值是一个新的函数,这个函数实现对历史函数调用的委托机制,在调用历史函数的同时,同时调用新特性对应的函数。
3、实现调用端的透明、无感知,对需要进行动态扩展的函数进行装饰,然后将装饰器函数的返回值,与原函数名进行绑定,即:原函数名 = 装饰器(原函数名)。
为了更加偷懒,Python还提供了装饰器语法糖,让装饰器的实现进一步简化。也是就@property同样的用法。
我们修改一下,改用@login_wrap的方式,需要稍微调整一下代码的顺序:
可以看到,我们无需手动调用login_wrap()来进行装饰了,只需要在需要装饰的函数定义之前添加上@login_wrap即可。
虽然写法不一样,但是我们需要知道,@login_wrap本质上,就是Python解释器帮我们执行了装饰器函数调用并赋值给原函数名的那行代码。
总结本文首先回归了开闭原则的相关内容,然后介绍了开闭原则的一个最佳实践——装饰器模式,最后简单介绍了Python中装饰器的用法。
需要说明的是,本文只是简单介绍了一下装饰器,装饰器中还有一些稍微复杂的内容,我们在后面的文章再一一介绍。
感谢您的拨冗阅读,希望对您学习Python有所帮助!