热门标签 | 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



推荐阅读
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社区 版权所有