Python装饰器实例讲解(一)
Python装饰器实例讲解(一)
多种角度讲述这个知识,这是个系列文章
但前后未必有一定的顺承关系
部分参考网络
本文以一个小案例引出装饰器的一些特点,不涉及理论,后面再谈
案例
写一个代码来求一个数是否是质数
def is_prime(x):
if x == 2 :
return True
elif x == 1 or x % 2 == 0 :
return False
for i in range(3, int(x ** 0.5) + 1, 2):
if x % i == 0:
return False
return True
写个代码来计算某个数值范围内有多少个质数
def get_prime_nums():
return len(list(filter(is_prime,range(2,50000))))
换一下,我们不是要学这个,我们要学装饰器
def get_prime_nums():
from time import time
start_time = time()
prime_nums = 0
for num in range(2,50000):
if is_prime(num):
prime_nums = prime_nums + 1
end_time = time()
print(f'统计花了{end_time-start_time}时间')
print(f'一共有{prime_nums}个质数') get_prime_nums()
# 统计花了0.025316476821899414时间
# 一共有5133个质数你在这里会发现一个潜在的需求,可能不光是你这么一个函数有统计时间的需求,其他函数一样有,但现在这种处理方法可能要在每个目标函数上去加那段时间处理的代码,非常麻烦,那有没有好的做法呢?答案就是装饰器。
装饰器
改造(对比下跟之前的区别)
获取质数个数函数,不需要统计时间
def get_prime_nums():
prime_nums = 0
for num in range(2,50000):
if is_prime(num):
prime_nums = prime_nums + 1
print(f'一共有{prime_nums}个质数')
写一个装饰器的函数(不用管为何这么写,以后会详细说明)
def count_time(func):
def wrapper():
from time import time
start_time = time()
func()
end_time = time()
print(f'统计花了{end_time-start_time}时间')
return wrapper
给要加时间的函数套上这个装饰器
@count_time
def get_prime_nums():
prime_nums = 0
... # 不重复了
再次执行get_prime_nums()效果跟之前是一样的
同样的你可以将这个装饰器运用到其他函数上去
@count_time
def get_odd_nums():
odd_nums = 0
for num in range(2,50000):
if num % 2 == 1:
odd_nums = odd_nums + 1
print(f'一共有{odd_nums}个奇数') get_odd_nums()
完了吗,没有。可能性还有很多,主要是被装饰函数的变化导致了装饰器本身要随之适应变化。
装饰器改造一
如果被装饰的函数有返回值呢?
@count_time
def get_prime_nums():
prime_nums = 0
for num in range(2,50000):
if is_prime(num):
prime_nums = prime_nums + 1
return prime_nums
此时你直接调用函数,而不改造装饰器的话,是无法得到这个数量的
get_prime_nums() # 统计花了0.032898664474487305时间 print(get_prime_nums())
# 统计花了0.039182424545288086时间
# None
改造装饰器
如何改造呢?你应该要去理解装饰器的运行原理(没那么复杂,但我们这个课不深入,仅作为案例给你展示)
def count_time(func):
def wrapper():
from time import time
start_time = time()
result = func() # 改动1: 用一个变量来接收func()的返回
end_time = time()
print(f'统计花了{end_time-start_time}时间')
return result # 改动2: return出去
return wrapper
此时你再执行
print(get_prime_nums())
# 统计花了0.054421424865722656时间
# 5133 就能看到这个返回值了完了吗?还没有,如果我们的被装饰函数有参数呢?
装饰器改造二
你的被装饰函数存在参数
@count_time
def get_prime_nums(end):
prime_nums = 0
for num in range(2,end):
if is_prime(num):
prime_nums = prime_nums + 1
return prime_nums print(get_prime_nums(50000))
其实在IDE中get_prime_nums(50000)就会提示你意外实参
执行结果
Traceback (most recent call last):
File "...\demo.py", line 37, in <module>
print(get_prime_nums(50000))
TypeError: wrapper() takes 0 positional arguments but 1 was given
这是初学者最困惑的地方了,等我们讲了原理(或者说诀窍)你应该就非常清楚为何会这样报错了
怎么修改呢?
def count_time(func):
def wrapper(*args): # 改动1: 增加一个不定参数
from time import time
start_time = time()
result = func(*args) # func也增加
end_time = time()
print(f'统计花了{end_time-start_time}时间')
return result
return wrapper
再次执行,就ok了
print(get_prime_nums(50000))
# 统计花了0.029825448989868164时间
# 5133
但是要注意,这样的话,如果你的被装饰函数是之前的没有参数的情况,是会报错的
# 回到过去
@count_time
def get_prime_nums():
prime_nums = 0
for num in range(2,50000):
if is_prime(num):
prime_nums = prime_nums + 1
return prime_nums
print(get_prime_nums(50000))
报错
Traceback (most recent call last):
File "...\demo.py", line 37, in <module>
print(get_prime_nums(50000))
File "...\demo.py", line 22, in wrapper
result = func(*args)
TypeError: get_prime_nums() takes 0 positional arguments but 1 was given 进程已结束,退出代码为 1但由于是*args,你改成多个参数倒是可以的
@count_time
def get_prime_nums(start,end):
prime_nums = 0
for num in range(start,end):
if is_prime(num):
prime_nums = prime_nums + 1
return prime_nums print(get_prime_nums(2,50000)) # 可以执行
如果你这样调用
print(get_prime_nums(start=2,end=50000))
报错
Traceback (most recent call last):
File "...\demo.py", line 37, in <module>
print(get_prime_nums(start=2,end=50000))
TypeError: wrapper() got an unexpected keyword argument 'start' 进程已结束,退出代码为 1可以这样修改你的装饰器
def count_time(func):
def wrapper(*args,**kwargs): # 加个关键字参数
from time import time
start_time = time()
result = func(*args,**kwargs) # 这样也要加
end_time = time()
print(f'统计花了{end_time-start_time}时间')
return result
return wrapper
说在最后
- 这个案例是入门的,讲解了装饰器的一些简单使用
- 但,留了一些坑,你可能未必知道为何要这么修改,装饰器是怎么调度的等等
- 且听下回分解
Python装饰器实例讲解(一)的更多相关文章
- python --装饰器内容讲解
python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能. 3.1 定义装饰器 ...
- Python 装饰器实例
retry 偶然看到一篇文章,想到了前几天的一个需求,git pull性能不稳,需要加入重试机制,正好这个装饰器的实例符合这样的场景. # coding:utf-8 import time impor ...
- python --装饰器通俗讲解
装饰器 什么是装饰器?:在不修改源代码和调用方式的基础上给其增加新的功能,多个装饰器可以装饰在同一个函数上 Python中的装饰器是你进入Python大门的一道坎; 装饰器特点: 不改变原函数原代码: ...
- Python装饰器与面向切面编程
今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数 ...
- Python装饰器讲解
Python装饰器讲解 定义:本质是函数,就是为其他函数添加附加功能.原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 import time def timmer(func ...
- Python学习:11.Python装饰器讲解(二)
回顾 上一节我们进行了Python简单装饰器的讲解,但是python的装饰器还有一部分高级的使用方式,这一节就针对python装饰器高级部分进行讲解. 为一个函数添加多个装饰器 今天,老板又交给你一个 ...
- Python的装饰器实例用法小结
这篇文章主要介绍了Python装饰器用法,结合实例形式总结分析了Python常用装饰器的概念.功能.使用方法及相关注意事项 一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让 ...
- 使用python装饰器计算函数运行时间的实例
使用python装饰器计算函数运行时间的实例 装饰器在python里面有很重要的作用, 如果能够熟练使用,将会大大的提高工作效率 今天就来见识一下 python 装饰器,到底是怎么工作的. 本文主要是 ...
- Python 装饰器(Decorators) 超详细分类实例
Python装饰器分类 Python 装饰器函数: 是指装饰器本身是函数风格的实现; 函数装饰器: 是指被装饰的目标对象是函数;(目标对象); 装饰器类 : 是指装饰器本身是类风格的实现; 类 ...
- python装饰器在接口自动化测试中的应用
在讲解装饰器在接口自动化测试项目的应用之前,我们先来介绍一下python装饰器到底是个什么 装饰器 说装饰器就不得不提一下函数这个一等公民了,在python中函数有几个特性先来了解一下 函数的一些特性 ...
随机推荐
- 支持JDK19虚拟线程的web框架,之五(终篇):兴风作浪的ThreadLocal
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos <支持JDK19虚拟线程的web框架>系列 ...
- 【红队技巧】Windows存储的密码获取
[红队技巧]Windows存储的密码获取 免责声明: 使用前提 支持版本 利用方式 参考: 免责声明: 本文章仅供学习和研究使用,严禁使用该文章内容对互联网其他应用进行非法操作,若将其用于非法目的,所 ...
- React实用插件收集
1.react-img-editor 图片编辑 demo: npm install react-img-editor -S 引入和使用 import ReactImgEditor from 'reac ...
- C++初阶(list容器+模拟实现)
list介绍 list的本质是一个带头的双向循环链表. 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每一个元素称为结点 ...
- Android网络请求(1)
Android网络请求(1) 安卓开发网络请求可谓是安卓开发的灵魂,如果你不会网络请求,那么你开发的应用软件就是一具没有灵魂的枯骨. 在安卓开发中进行网络请求和java中的网络请求有异曲同工之 ...
- React基础学习知识笔记
React项目的核心就是index.js 第一个程序 import React from 'react'; import ReactDOM from 'react-dom';ReactDOM.rend ...
- ArcEngine 序列化AO对象
ArcEngine中只要是继承了IPersistStream接口的对象均可调用ArcEngine中的类库进行序列化和反序列化.一般我们会序列化成xml格式,作为字符串存储,需要的时候,反序列化为对象. ...
- HCIE Routing&Switching之MPLS静态LSP配置
前文我们了解了MPLS基础理论部分,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16928096.html:今天我们来聊一聊MPLS静态LSP配置相关话题: ...
- ChatGPT 会开源吗?
最近,我被一款叫做 ChatGPT 的人工智能(AI)聊天机器人,刷屏了.网上有人说它是搜索引擎杀手,还有人说它将取代程序员... 最后,我还是没扛住铺天盖地的赞美,跑去注册了个账号,抱着调侃&quo ...
- Java单例模式的最佳实践?
"读过书,--我便考你一考.茴香豆的茴字,怎样写的?"--鲁迅<孔乙己> 0x00 大纲 目录 0x00 大纲 0x01 前言 0x02 单例的正确性 new关键字 c ...