热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Python进阶笔记:深入理解装饰器、生成器与迭代器的应用

本文深入探讨了Python中的装饰器、生成器和迭代器的应用。装饰器本质上是一个函数,用于在不修改原函数代码和调用方式的前提下为其添加额外功能。实现装饰器需要掌握闭包、高阶函数等基础知识。生成器通过`yield`语句提供了一种高效生成和处理大量数据的方法,而迭代器则是一种可以逐个访问集合中元素的对象。文章详细解析了这些概念的原理和实际应用案例,帮助读者更好地理解和使用这些高级特性。
1.装饰器

定义:本质是函数,(装饰其他函数)就是为其他函数添加附加功能

原则:1.不能修改被装饰的函数的源代码

   2.不能修改被装饰的函数的调用方式

实现装饰器的知识储备:

  1.      函数即“变量”

  2.      高阶函数

           a.      把一个函数名当做实参传给另一个函数(在不修改被装饰函数源代码的情况下为期添加功能)

           b.      返回值中包含函数名(不修改函数的调用方式)

  3.      嵌套函数

  4.      高阶函数+嵌套函数=装饰器

 

1.1 实现简单的装饰器

1 def outer(fun):
2 def warper():
3 print("outer 1")
4 fun() #相当于把下边两个函数当参数传入进来
5 print("outer 3")
6 return warper
7
8 @outer
9 def test1():
10 print("test1 2")
11 @outer
12 def test2():
13 print("test2 2")
14 test1()
15 test2()
-----------------结果-----------------
16 outer 1
17 test1 2
18 outer 3
19 outer 1
20 test2 2
21 outer 3

1.2 装饰器传参

简单难度的传参

1 def outer(fun):
2 def warper(*args,**kwargs):
3 print("加的第一个功能在函数之前")
4 fun(*args,**kwargs)
5 print("加的第二个功能在函数之后")
6 return warper
7
8 @outer
9 def test1(*args,**kwargs):
10 print(args,kwargs)
11 return
12
13
14 test1(123,"waa","yyy",name="wsy",Age=18)
15 ----------------结果----------------------
16 加的第一个功能在函数之前
17 (123, 'waa', 'yyy') {'name': 'wsy', 'Age': 18}
18 加的第二个功能在函数之后

中等难度的传参

加入time模块记录执行时间

import timedef timer(func):def deco(*args,**kwargs):start = time.time()print("装饰器:功能1")func(*args,**kwargs)print("装饰器:功能2")end = time.time()print("func run time is %s" %(end - start))return deco@timer
def test1():time.sleep(1)print("in the test1")
@timer
def test2(*args,**kwargs):time.sleep(1)print("in the test1",args,kwargs)test1()
test2(
1,2,3,4,name="wsy",age=20)
--------------------结果------------------------
装饰器:功能1
in the test1
装饰器:功能2
func run time
is 1.0000572204589844
装饰器:功能1
in the test1 (1, 2, 3, 4) {'name': 'wsy', 'age': 20}
装饰器:功能2
func run time
is 1.0000572204589844

多重认证  第一个页面认证成功直接进入其他两个页面

1 user,passwd = "wsy","123"
2 def auth(auth_type):
3 print("auth func:",auth_type)
4 def outer_wrapper(func):
5 def wrapper(*args,**kwargs):
6 print("wrapper func args:", *args,**kwargs)
7 if auth_type == "local": # 如果装饰器参数是local 就
8 username = input("Username:").strip() # 输入用户名
9 password = input("Password:").strip() # 输入密码
10 if user == username and passwd == password: # 判断如果正确
11 print("\033[32;1mUser has passwd authentication\033[0m")
12 res = func(*args,**kwargs) #from home
13 print("-------after authenticaion")
14 return res
15 else:
16 exit("\033[31;1mInvalid username or password \033[0m")
17 elif auth_type == "ldap": # 如果装饰器参数是ldap
18 print("hehe")
19 return wrapper
20 return outer_wrapper
21
22
23 def index():
24 print("welcome to index page")
25
26 @auth(auth_type="local")
27
28 def home():
29 print("welcome to home page")
30 return "from home"
31 @auth(auth_type="ldap")
32 def bbs():
33 print("welcome to bbs page")
34 index()
35 print (home())
36 bbs()
37 -----------------------结果-----------------------
38 auth func: local
39 auth func: ldap
40 welcome to index page
41 wrapper func args:
42 Username:wsy
43 Password:123
44 User has passwd authentication
45 welcome to home page
46 -------after authenticaion
47 from home
48 wrapper func args:
49 hehe

 

 2.生成器

生成器

通过列表生成式,我们可以直接创建一个列表。但是受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量空间,在py中,这种一遍循环一遍计算的机制,称为生成器

终端命令行执行>>> a = [1,2,3]>>> [i*2 for i in range(10)]-----------------输出结果-----------------[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

列表生成器

终端执行:
>>> ( i*2 for i in range(10))
-------------------输出结果------------------------ at 0x00000000021859E8>>>>b = ( i*2 for i in range(10))>>> for i in b:... print(i)
-------------------输出结果-------------------------024681012141618

range

生成器 只有在调用时才会生成相应的数据       

只记住当前位置

只有一个__next__()方法

1 a = (i*2 for i in range(100))
2 print(a.__next__())
3 print(a.__next__())
4 print(a.__next__())
5 print(a.__next__())
6 ---------------输出结果-------------------
7 0
8 2
9 4
10 6

我们创建了一个generator(生成器)后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误

generator(生成器)非常强大,如果推算的算法比较复杂,用列斯列表生成式的for循环无法实现的时候,还可以用函数来实现

2.1 斐波那契数列

菲波纳契数列,除第一个和第二个数外,任意一个数都可由前两个数相加得到

1 def fib(max):
2 n, a, b = 0, 0, 1
3 while (n < max):
4 print(b)
5 a, b &#61; b, a&#43;b
6 n &#43;&#61; 1
7
8 fib(10)
9 ---------------------------------------
10 1
11 1
12 2
13 3
14 5
15 8
16 13
17 21
18 34
19 55

2.2 yield

1 def fib(max):
2 n,a,b &#61; 0,0,1
3 while (n <max):
4 yield b
5 a,b &#61; b, a&#43;b
6 n &#43;&#61; 1
7 f &#61; fib(10)
8 for i in range(10):
9 print(f.__next__())
10 --------------输出结果-----------------
11 1
12 1
13 2
14 3
15 5
16 8
17 13
18 21
19 34
20 55

2.3 yield 实现单线程并行

1 import time
2 def consumer(name):
3 while True:
4 baozi &#61; yield
5 print("包子[%s]来了&#xff0c;被[%s]吃了" %(baozi,name))
6
7
8 c &#61; consumer("wsy")
9 c.__next__()
10
11 def producer(name):
12 c &#61; consumer(&#39;&#39;)
13 c2 &#61; consumer(&#39;&#39;)
14 c.__next__()
15 c2.__next__()
16 print("开始吃")
17 for i in range(10):
18 time.sleep(1)
19 print("做了1个包子&#xff0c;分两半")
20 c.send(i)
21 c2.send(i)
22 producer("wsy")
23 ---------------------------结果------------------------
24 开始吃
25 做了1个包子&#xff0c;分两半
26 包子[0]来了&#xff0c;被[猫]吃了
27 包子[0]来了&#xff0c;被[狗]吃了
28 做了1个包子&#xff0c;分两半
29 包子[1]来了&#xff0c;被[猫]吃了
30 包子[1]来了&#xff0c;被[狗]吃了
31 做了1个包子&#xff0c;分两半
32 包子[2]来了&#xff0c;被[猫]吃了
33 包子[2]来了&#xff0c;被[狗]吃了
34 做了1个包子&#xff0c;分两半
35 包子[3]来了&#xff0c;被[猫]吃了
36 包子[3]来了&#xff0c;被[狗]吃了
37 做了1个包子&#xff0c;分两半
38 包子[4]来了&#xff0c;被[猫]吃了
39 包子[4]来了&#xff0c;被[狗]吃了
40 做了1个包子&#xff0c;分两半
41 包子[5]来了&#xff0c;被[猫]吃了
42 包子[5]来了&#xff0c;被[狗]吃了
43 做了1个包子&#xff0c;分两半
44 包子[6]来了&#xff0c;被[猫]吃了
45 包子[6]来了&#xff0c;被[狗]吃了
46 做了1个包子&#xff0c;分两半
47 包子[7]来了&#xff0c;被[猫]吃了
48 包子[7]来了&#xff0c;被[狗]吃了
49 做了1个包子&#xff0c;分两半
50 包子[8]来了&#xff0c;被[猫]吃了
51 包子[8]来了&#xff0c;被[狗]吃了
52 做了1个包子&#xff0c;分两半
53 包子[9]来了&#xff0c;被[猫]吃了
54 包子[9]来了&#xff0c;被[狗]吃了

吃包子例子
 3.迭代器

我们已经知道&#xff0c;可以直接作用于for循环的数据类型有一下几种&#xff1a;

1.集合数据类型&#xff0c;如list&#xff0c;tuple&#xff0c;dict&#xff0c;set&#xff0c;str等

2.生成器&#xff0c;包括生成器和带yield的generator function

这些可以直接作用于for循环的对象统称为可迭代对象&#xff1a;Iterable

可以使用isinstance()判断一个对象是否是Iterable对象&#xff1a;

1 命令行
2 >>> from collections import Iterable
3 >>> isinstance([],Iterable)
4 True
5 >>> isinstance({},Iterable)
6 True
7 >>> isinstance(&#39;abc&#39;,Iterable)
8 True
9 >>> isinstance((x for x in range(10)),Iterable)
10 True
11 >>> isinstance(100,Iterable)
12 False

判断是否为iterable对象

而生成器不但可以作用于for循环&#xff0c;还可以被next()函数不断调用并返回下一个值&#xff0c;知道最后抛出StopIteration错误表示无法继续返回下一个值了。

*可以被next()函数调用并不断返回下一个值得对象成为迭代器&#xff1a;Iterator

可以使用isinstance()判断一个对象是否是Iterator对象&#xff1a;

 

#结论&#xff1a;生成器一定是迭代器 迭代器不一定是生成器

生成器都是Iterator&#xff08;迭代器&#xff09;对象&#xff0c;但list&#xff0c;dict&#xff0c;str虽然是iterable&#xff08;可迭代&#xff09;却不是Iterator&#xff08;迭代器&#xff09;

把list&#xff0c;dict&#xff0c;str等Iterable变成Iterator可以使用iter()函数&#xff1a;

1 >>> a
2 [1, 2, 3]
3 >>> iter(a)
4
5 >>> b &#61; iter(a)
6 >>> b.__next__()
7 1
8 >>> b.__next__()
9 2
10 >>> b.__next__()

只要有next()函数 一定是迭代器

 

 

 

 

 

 

 


转:https://www.cnblogs.com/wsy1030/p/9015817.html



推荐阅读
  • 【图像分类实战】利用DenseNet在PyTorch中实现秃头识别
    本文详细介绍了如何使用DenseNet模型在PyTorch框架下实现秃头识别。首先,文章概述了项目所需的库和全局参数设置。接着,对图像进行预处理并读取数据集。随后,构建并配置DenseNet模型,设置训练和验证流程。最后,通过测试阶段验证模型性能,并提供了完整的代码实现。本文不仅涵盖了技术细节,还提供了实用的操作指南,适合初学者和有经验的研究人员参考。 ... [详细]
  • 提升Android开发效率:Clean Code的最佳实践与应用
    在Android开发中,提高代码质量和开发效率是至关重要的。本文介绍了如何通过Clean Code的最佳实践来优化Android应用的开发流程。以SQLite数据库操作为例,详细探讨了如何编写高效、可维护的SQL查询语句,并将其结果封装为Java对象。通过遵循这些最佳实践,开发者可以显著提升代码的可读性和可维护性,从而加快开发速度并减少错误。 ... [详细]
  • Python内置模块详解:正则表达式re模块的应用与解析
    正则表达式是一种强大的文本处理工具,通过特定的字符序列来定义搜索模式。本文详细介绍了Python内置的`re`模块,探讨了其在字符串匹配、验证和提取中的应用。例如,可以通过正则表达式验证电子邮件地址、电话号码、QQ号、密码、URL和IP地址等。此外,文章还深入解析了`re`模块的各种函数和方法,提供了丰富的示例代码,帮助读者更好地理解和使用这一工具。 ... [详细]
  • 本文探讨了利用Python实现高效语音识别技术的方法。通过使用先进的语音处理库和算法,本文详细介绍了如何构建一个准确且高效的语音识别系统。提供的代码示例和实验结果展示了该方法在实际应用中的优越性能。相关文件可从以下链接下载:链接:https://pan.baidu.com/s/1RWNVHuXMQleOrEi5vig_bQ,提取码:p57s。 ... [详细]
  • 本文总结了JavaScript的核心知识点和实用技巧,涵盖了变量声明、DOM操作、事件处理等重要方面。例如,通过`event.srcElement`获取触发事件的元素,并使用`alert`显示其HTML结构;利用`innerText`和`innerHTML`属性分别设置和获取文本内容及HTML内容。此外,还介绍了如何在表单中动态生成和操作``元素,以便更好地处理用户输入。这些技巧对于提升前端开发效率和代码质量具有重要意义。 ... [详细]
  • 本文探讨了 Java 中 Pair 类的历史与现状。虽然 Java 标准库中没有内置的 Pair 类,但社区和第三方库提供了多种实现方式,如 Apache Commons 的 Pair 类和 JavaFX 的 javafx.util.Pair 类。这些实现为需要处理成对数据的开发者提供了便利。此外,文章还讨论了为何标准库未包含 Pair 类的原因,以及在现代 Java 开发中使用 Pair 类的最佳实践。 ... [详细]
  • 在前文探讨了Spring如何为特定的bean选择合适的通知器后,本文将进一步深入分析Spring AOP框架中代理对象的生成机制。具体而言,我们将详细解析如何通过代理技术将通知器(Advisor)中包含的通知(Advice)应用到目标bean上,以实现切面编程的核心功能。 ... [详细]
  • 具备括号和分数功能的高级四则运算计算器
    本研究基于C语言开发了一款支持括号和分数运算的高级四则运算计算器。该计算器通过模拟手算过程,对每个运算符进行优先级标记,并按优先级从高到低依次执行计算。其中,加减运算的优先级最低,为0。此外,该计算器还支持复杂的分数运算,能够处理包含括号的表达式,提高了计算的准确性和灵活性。 ... [详细]
  • 本文探讨了如何利用 jQuery 的 JSONP 技术实现跨域调用外部 Web 服务。通过详细解析 JSONP 的工作原理及其在 jQuery 中的应用,本文提供了实用的代码示例和最佳实践,帮助开发者解决跨域请求中的常见问题。 ... [详细]
  • 本文探讨了 Kafka 集群的高效部署与优化策略。首先介绍了 Kafka 的下载与安装步骤,包括从官方网站获取最新版本的压缩包并进行解压。随后详细讨论了集群配置的最佳实践,涵盖节点选择、网络优化和性能调优等方面,旨在提升系统的稳定性和处理能力。此外,还提供了常见的故障排查方法和监控方案,帮助运维人员更好地管理和维护 Kafka 集群。 ... [详细]
  • iOS 设备唯一标识获取的高效解决方案与实践
    在iOS 7中,苹果公司再次禁止了对MAC地址的访问,使得开发者无法直接获取设备的物理地址。为了在开发过程中实现设备的唯一标识,苹果推荐使用Keychain服务来存储和管理唯一的标识符。此外,还可以结合其他技术手段,如UUID和广告标识符(IDFA),以确保设备的唯一性和安全性。这些方法不仅能够满足应用的需求,还能保护用户的隐私。 ... [详细]
  • 本文深入解析了Java 8并发编程中的`AtomicInteger`类,详细探讨了其源码实现和应用场景。`AtomicInteger`通过硬件级别的原子操作,确保了整型变量在多线程环境下的安全性和高效性,避免了传统加锁方式带来的性能开销。文章不仅剖析了`AtomicInteger`的内部机制,还结合实际案例展示了其在并发编程中的优势和使用技巧。 ... [详细]
  • 2018年9月21日,Destoon官方发布了安全更新,修复了一个由用户“索马里的海贼”报告的前端GETShell漏洞。该漏洞存在于20180827版本的某CMS中,攻击者可以通过构造特定的HTTP请求,利用该漏洞在服务器上执行任意代码,从而获得对系统的控制权。此次更新建议所有用户尽快升级至最新版本,以确保系统的安全性。 ... [详细]
  • 本课程深入探讨了 Python 中自定义序列类的实现方法,涵盖从基础概念到高级技巧的全面解析。通过实例演示,学员将掌握如何创建支持切片操作的自定义序列对象,并了解 `bisect` 模块在序列处理中的应用。适合希望提升 Python 编程技能的中高级开发者。 ... [详细]
  • 总数 | 小规模算法动态规划第3讲:LeetCode 62 不同路径详解 | 从自顶向下到自底向上的动态规划方法分析
    总数 | 小规模算法动态规划第3讲:LeetCode 62 不同路径详解 | 从自顶向下到自底向上的动态规划方法分析 ... [详细]
author-avatar
xiumin金珉锡吧1b
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有