Python函数装饰器的使用详解
作者:DgLink 发布时间:2023-04-30 14:47:24
装饰器
装饰器的定义
关于装饰器的定义,我们先来看一段github上大佬的定义:
Function decorators are simply wrappers to existing functions.
In the context of design patterns,decorators dynamically alter the functionality of a function, method or class without having to directly use subclasses.
This is ideal when you need to extend the functionality of functions that you don’t want to modify.
We can implement the decorator pattern anywhere, but Python facilitates the implementation by providing much more expressive features and syntax for that.
这段话的最主要的意思就是:
函数装饰器是给已有函数的简单容器。
在原有的代码环境下,它能够动态地改变一个函数的功能,或者不直接使用子类而改变方法和类。
当你不想修改源代码但又想实现功能上的扩充的时候,这是一个不错的方法。
我们可以在任何情况下使用装饰器样式,同时Python通过提供更多强力的属性和语法来帮助装饰器的使用。
装饰器的意义
在上文之中其实我们已经知道了装饰器的意义,
可以不需要修改源代码就能够直接做到功能的扩充,但是为此我们需要付出的是更多的编写时间,
而且更为重要的是,通过修改源码实现功能的增加往往会改变接口传递的参数,可一个项目之中往往存在许多接口这也代表着你可能需要多次更改接口参数,
这个工作量,我们可不干!
装饰器的使用
无参装饰器
无参装饰器是最为基础的装饰器,
其根本原因在于装饰的函数对象不需要仍和的参数;
接下来示范一下最简单的装饰器是如何实现的:
def greeting():# 定义一个最基本的无参函数
return "Hello,读者老爷们!"
# 现在我需要实现的要求是:让输出的内容变为<p>"Hello,读者老爷们"<p>
# 在不直接更改greeting函数的前提下,我们需要使用无参装饰器
def decorator_p(func):# 用于接收一个函数
def wrapper():
return f'<p>{func()}<p>'
return wrapper
decorator = decorator_p(greeting)# 调用decorator_p,并且用一个decorator接收返回值
print(decorator())
<p>Hello,读者老爷们!<p>
以上就是输出的结果
但是这个结果我其实并不满意,
因为完成了功能附加之后我们居然还需要再使用decorator = decorator_p(greeting)
来接收一下,而且这样的话调用方式就不再是原本的greeting()
了,而是decorator()
这两者对于追求高效优雅的Python来说已经提供解决方法了,
让我娓娓道来:
# 针对于调用方式而言,我们首先想到的解决方法是
greeting = decorator_p(greeting)# 将原本的greeting函数传给decorator_p,而重命名
print(greeting())# 输出结果与原来相同
但这个仍然可以改进,这就要使用到Python提供的@
,但使用这种方法必须注意书写的顺序,因此代码必须这样更改:
def decorator_p(func):
def wrapper():
return f'<p>{func()}<p>'
return wrapper
@decorator_p# 效果等同于 greeting = decorator_p(greeting)
def greeting():
return "Hello,读者老爷们!"
print(greeting())
<p>Hello,读者老爷们!<p>
得到的结果是我们想要的,但使用这种方法顺序格外重要,
如果这样书写,则会给出报错:
@decorator_p#使用这个@后,将会开始向上寻找decorator_p这个函数
def greeting():
return "Hello,读者老爷们!"
print(greeting())
def decorator_p(func):
def wrapper():
return f'<p>{func()}<p>'
return wrapper
NameError: name ‘decorator_p’ is not defined
给出的报错原因是因为没有找到decorator_p
这个函数,
可明明我们已经完成这个函数的定义了,
所以我们可以得到的结论便是:
当使用@时,就会开始向上寻找函数,当找不到函数的时候就会报错
有参装饰器
接下来介绍一下有参装饰器,就是指需要传递参数的装饰器,
上文之中其实已经介绍过了关于无参装饰器的使用,而有参装饰器也并没有多难,
来一个示范:
def decorator(func):
def wrapper(name):
return f'<p>{func(name)}<p>'
return wrapper
@decorator
def greeting(name):
return f"Hello,{name}!"
print(greeting('读者老爷'))# 传递一个参数
<p>Hello,读者老爷!<p>
实例练习
OK,经过上文介绍所有读者应该又会处于似懂非懂的状态,
那么秉持一文一练的理念,我们接下来将通过编写登录功能,仔细看一下使用装饰器和不适用装饰器的区别。
在开始之前,我创建了一个文本文件夹,将会用于模拟用户储存的信息,内容如下:
sign_in.txt
Joseph:Jostar
Jonasen:Jostar
Kujo:Jotaro
Jolin:Kujo
Diavollo:Doppio
需求清单:
1)用户账户只能够由数字和英文组成,长度不超过16字符;
2)用户密码只能够由数字和英文组成,长度不超过16字符;
3)用户拥有4次输入的机会。
# 不使用任何函数
# 无装饰器 登录功能
count = 1# 用于计算使用次数
while count < 5:
user_account = input('请输入你的账号')
if user_account.isalnum() and len(user_account) <= 16:# 判断账号长度和组成
user_keyword = input('请输入你的密码')
if user_keyword.isalnum() and len(user_keyword) <= 16:# 判断密码的长度和组成
with open('sign_in.txt','r') as file:# 打开登录文件核对登录信息
for line in file:
r_name, r_keyword = line.strip().split(':')
if r_name == user_account and r_keyword == user_keyword:
print('登录成功')
break
else:
count += 1
print(f'账号密码不匹配,还剩下{5-count}次机会')
continue
break
else:
count += 1
print(f'密码输入错误,还剩下{5-count}次机会')
continue
else:
count += 1
print(f'输入账号错误,还剩下{5-count}次机会')
continue
else:
print('机会已用完')
程序员之间总是流传着这么一个梗,
开发总是会看着一串代码愣愣出神说道:“这究竟是谁写的代码,像坨屎。”
然后过了良久突然说“好像是我自己写的。”
接下来我们需要做的就是改良,使用装饰器将其改良,
我们的源代码将其固定成这样:
# 只是单纯的用户输入,
# 而后我们要在此基础上不断优化,为其添加上判断长度、限制次数的功能
def input_signup():
user_name = input('请输入账户名字')
user_keyword = input('请输入账户密码')
return user_name, user_keyword
def passing_func(func1, func2, func3):
def wrapper(func4):
def decorator():
count = 1
while count < 5:
name, keyword = func4()
check = func1(name, keyword)
if check is not True:
count += 1
print(f'你还剩下{5-count}次登录机会')
continue
check = func2(name, keyword)
if check is not True:
count += 1
print(f'你还剩下{5-count}次登录机会')
continue
check = func3(name, keyword)
if check is not True:
count += 1
print(f'你还剩下{5 - count}次登录机会')
continue
else:
break
return decorator
return wrapper
def limit_len(name, keyword, length=16):
'''
用于判断用户名字和密码的长度是否符合标准
:param name:用于接受用户的名字
:param keyword:用于接受用户的密码
:param length:默认参数为16
:return: check:用于判断输入是否符合长度标准,详细请见装饰器中的使用
'''
check = True
if len(name) > length or len(keyword) > length:
print('账号名或密码长度不符合规范')
check = False
return check
return check
def limit_composition(name, keyword):
'''
用于判断用户名字和密码的组成是否符合标准
:param name:用于接受用户的名字
:param keyword:用于接受用户的密码
:return: check:用于判断输入是否符合长度标准,详细请见装饰器中的使用
'''
check = True
if name.isalnum() is not True or keyword.isalnum() is not True:
print('账号名或密码组成不符合规范')
check = False
return check
return check
def verify_useinfo(name, keyword):
'''
用于检验用户输入的账号和密码是否符合文件中储存的
:param name:用于接受用户的名字
:param keyword:用于接受用户的密码
:return: check:用于判断输入是否符合长度标准,详细请见装饰器中的使用
'''
check = True
with open('sign_in.txt','r') as file:
for line in file:
r_name, r_keyword = line.strip().split(':')
if r_name == name and r_keyword == keyword:
print('登录成功,欢迎使用')
return check
else:
check = False
print('账号密码错误,请重新输入')
return check
@passing_func(limit_len, limit_composition, verify_useinfo)
def input_signup():
user_name = input('请输入账户名字')
user_keyword = input('请输入账户密码')
return user_name, user_keyword
来源:https://blog.csdn.net/DgLink/article/details/122438850
猜你喜欢
- 引言欢迎来到我们的系列博客《Python全景系列》!在这个系列中,我们将带领你从Python的基础知识开始,一步步深入到高级话题,帮助你掌握
- 刚刚登甲发来一个文章,看到只要一行代码,就可以把IE6弄死.<style>*{position:relative}&
- 通过学习ASP明明白白你的If语句流程。If condition Then [statements1]E
- python运行其他程序的实现方法  
- 在进行CSS网页布局的时候,我们经遇到刷新要保留表单里内容的时候,习惯的做法使用cookie,但是那样做实在是很麻烦,css中的behavi
- 本文实例为大家分享了python tkinter库实现气泡屏保和锁屏的具体代码,供大家参考,具体内容如下显示效果如下:代码: im
- 在使用Python处理数据时,经常需要对数据筛选。这是在对时间筛选时,判断两列时间是否相差一年,如果是,则返回符合条件的所有列。data原始
- 简介壁纸的选择其实很大程度上能看出电脑主人的内心世界,有的人喜欢风景,有的人喜欢星空,有的人喜欢美女,有的人喜欢动物。然而,终究有一天你已经
- 什么是MobileNetV2模型MobileNet它哥MobileNetV2也是很不错的呢MobileNet模型是Google针对手机等嵌入
- 一朋友委托我帮他投票,地址在: http://publish.sina.com.cn/04/13/413/search.php 投票的链接是
- 假设mysql 安装在c:盘,mysql数据库的用户名是root,密码是123456,数据库名是database_name,在d:盘根目录下
- 以下所描述无理论依据,纯属经验谈。MySQL使用4.1以上版本,管他是什么字符集,一律使用默认。不用去设置MySQL。然后举个使用GB231
- 我就废话不多说了,大家还是直接看代码吧!#加载keras模块from __future__ import print_functionimp
- DataList Web 服务器控件 通过使用模板显示数据源中的项。通过操作组成 DataList
- 每次抽取后都重新洗牌。计算10000次随机抽取可得到同花的几率。我做的比较复杂,分别累计了四种花色分别出现了几次import randoml
- Adodb.Stream是ADO的Stream对象,提供存取二进制数据或者文本流,从而实现对流的读、写和管理等操作. 组件:&qu
- python的try语句有两种风格一是处理异常(try/except/else)二是无论是否发生异常都将执行最后的代码(try/finall
- 系统环境:64位win7企业版python2.7.102016.08.16修改内容:1)read_until()函数是可以设置timeout
- 闭包account=0def atm(num,flag): global account  
- 自动发送邮件能应用于许多场景,比如我想要知道股票策略中的股票池是否有实时的更新,这时候如果再拉一遍数据,跑一遍脚本,实在是太浪费时间了。为什