装饰器-迭代器-生成器
# 1. 装饰器
**装饰器:**装饰它人的工具,装饰器本身可以是任意可调用对象,被装饰的对象本身也可以是任意可调用对象
为什么要使用装饰器?
- 装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展
- 装饰器的本质:就是一个闭包函数
装饰器的开放封闭原则:
- 对扩展是开放的
- 对修改是封闭的
装饰器的遵循的原则:
- 不修改被装饰对象的源代码
- 不修改被调用对象的调用方式
@装饰器名,必须写在被装饰对象的正上方,并且是单独一行
import time
def timmer(func):
# func=index或者home
def wrapper():
start=time.time()
func()
stop=time.time()
print('run time is %s' %(stop-start))
return wrapper
@timmer # index=timmer(index)把下面被装饰函数index当做参数传递给timmer
def index():
time.sleep(3)
print('welcome to index')
@timmer # home=timmer(home)
def home():
time.sleep(2)
print('welcome to home page')
index()
home()
import time
def timmer(func):
def wrapper(*args,**kwargs): # 能接受任意参数
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print('run time is %s' %(stop-start))
return res
return wrapper
@timmer # index=timmer(index)
def index():
time.sleep(3)
print('welcome to index')
return 123
@timmer # home=timmer(home)
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)
# res=index() #res=wrapper()
# print(res)
res1=home('egon') #wrapper('egon')
print(res1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
实现用户认证功能:
import time
from functools import wraps
current_user={'user':None}
def timmer(func):
@wraps(func)
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print('run time is %s' %(stop-start))
return res
return wrapper
def auth(auth_type='file'):
def deco(func):
def wrapper(*args, **kwargs):
if auth_type == 'file':
if current_user['user']:
return func(*args, **kwargs)
name = input('name: ').strip()
password = input('password: ').strip()
with open('db.txt', encoding='utf-8') as f:
user_dic = eval(f.read())
if name in user_dic and password == user_dic[name]:
res = func(*args, **kwargs)
current_user['user'] = name
return res
else:
print('user or password error')
elif auth_type == 'mysql':
print('mysql')
elif auth_type == 'ldap':
print('ldap')
else:
print('not valid auth_type')
return wrapper
return deco
@timmer #index=timmer(wrapper)
@auth() # @deco #index=deco(index) #wrapper
def index():
'''这是index函数'''
time.sleep(3)
print('welcome to index')
return 123
# print(index.__doc__)
# print(help(index))
index()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 2. 迭代器
迭代:是一个重复的过程,每一次重复,都是基于上一次的结果而来
#对于像字符串、列表、元组这样的有序数据类型,可以根据索引取值
l=['a','b','c','d']
count=0
while count < len(l):
print(l[count])
count+=1
#对于字典、集合这样的无序数据类型,无法根据索引取值,所以就要用迭代器
dic={'name':'egon','sex':'m',"age":18}
iter_dic=iter(dic)
while True:
try:
k=next(iter_dic)
print(k,dic[k])
except StopIteration: #使用try:except可以捕捉到异常并跳过
break
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
可迭代对象iterable:凡是对象下有_iter__方法:对象._iter,该对象就是可迭代对象
s='hello'
l=['a','b','c','d']
t=('a','b','c','d')
dic={'name':'egon','sex':'m',"age":18}
set1={1,2,3}
f=open('db.txt')
1
2
3
4
5
6
2
3
4
5
6
**迭代器对象:**可迭代对象执行内置的__iter__方法,得到的结果就是迭代器对象
- 有_iter_,执行得到仍然是迭代本身
- 有_next_
dic={'name':'egon','sex':'m',"age":18}
i=dic.__iter__()
# print(i) #iterator迭代器
# i.__next__() #next(i)
print(next(i))
print(next(i))
print(next(i))
print(next(i)) #StopIteration 当迭代器对象里的值取完之后会抛出异常
l=['a','b','c','d']
i=l.__iter__()
print(next(i))
print(next(i))
print(next(i))
print(next(i))
print(next(i)) #StopIteration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
迭代器对象的优点
- 提供了一种统一的(不依赖于索引的)迭代方式
- 迭代器本身,比起其他数据类型更省内存
迭代器对象的缺点
- 一次性,只能往后走,不能回退,不如索引取值灵活
- 无法预知什么时候取值结束,即无法预知长度
迭代器原理类似for循环原理
l=['a','b','c','d']
for item in l: #iter_l=l.__iter__() # for循环的本质是先使用__iter__方法把可迭代对象变成迭代器对象,然后调用__next__逐个取出值,取值完成后不使用try:except也不会抛出异常
print(item)
1
2
3
2
3
判断可迭代对象与迭代器对象方法:
- 判断内部是不是实现了__next__方法
- Iterable 判断是不是可迭代对象
- Iterator 判断是不是迭代器
from collections import Iterable,Iterator
s='hello'
l=['a','b','c','d']
t=('a','b','c','d')
dic={'name':'egon','sex':'m',"age":18}
set1={1,2,3}
f=open('a.txt')
#isinstance、Iterable判断数据是否是可迭代对象
print(isinstance(s,Iterable))
print(isinstance(l,Iterable))
print(isinstance(t,Iterable))
print(isinstance(dic,Iterable))
print(isinstance(set1,Iterable))
print(isinstance(f,Iterable))
#isinstance、Iterator判断数据是否是迭代器对象
print(isinstance(s,Iterator))
print(isinstance(l,Iterator))
print(isinstance(t,Iterator))
print(isinstance(dic,Iterator))
print(isinstance(set1,Iterator))
print(isinstance(f,Iterator))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
判断range函数和map函数
map1=map(abs,[1,-2,3,-4])
print(isinstance(map1,Iterable))
print(isinstance(map1,Iterator))#map方法自带迭代器
s=range(100)#是一个可迭代的,但是不是迭代器
print(isinstance(s,Iterable))
print(isinstance(s,Iterator))
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 3. 生成器
生成器的定义:在函数内部包含yield关键字,那么该函数执行的结果是生成器
生成器的好处:不会一下在内存中生成太多的数据
生成器的本质就是迭代器
python中提供的生成器:生成器函数 和 生成器表达式
yield的功能:
- 把函数的结果做成迭代器(以一种优雅的方式封装好_iter_,_next_)
- 函数暂停与再继续运行的状态是由yield控制
def func():
print('first')
yield 11111111
print('second')
yield 2222222
print('third')
yield 33333333
print('fourth')
g=func()
print(next(g)) #函数运行到第一个yield时会返回1111,并暂停函数的运行
print('======>')
print(next(g)) #再次运行函数,函数会接着往下运行,返回22222,并暂停函数
print('======>')
print(next(g))
print('======>')
print(next(g))
for i in g: #i=iter(g)
print(i)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
应用场景1:
假如我想让工厂给学生做校服,生产2000000件衣服,我和工厂一说,工厂应该是先答应下来,然后再去生产,我可以一件一件的要,也可以根据学生一批一批的找工厂拿。而不能是一说要生产2000000件衣服,工厂就先去做生产2000000件衣服,等回来做好了,学生都毕业了。。。
def make_cloth():
for i in range(1,20000):
yield '第%s件衣服'%(i)
ret = make_cloth()
print(next(ret))
print(next(ret))
print(next(ret))
for i in range(100):
print(next(ret))
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
应用场景2:
计算移动平均值
# 必须先用next再用send
def average():
total=0 #总数
day=0 #天数
average=0 #平均数
while True:
day_num = yield average #average=0
total += day_num
day += 1
average = total/day
avg=average() #直接返回生成器
next(avg)#激活生成器,avg.send(),什么都不传的时候send和next的效果一样
print(avg.send(10))
print(avg.send(20))#send 1.传值 2.next
print(avg.send(30))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
应用场景3:
带装饰器的计算移动平均值
# 让装饰器去激活
def wrapper(func):
def inner(*args,**kwargs):
ret = func(*args,**kwargs)
next(ret)
return ret
return inner
@wrapper
def average():
total=0 # 总数
day=0 # 天数
average=0 #平均数
while True:
day_num = yield average # average=0
total += day_num
day += 1
average = total/day
ret=average() #直接返回生成器
print(ret.send(10))
print(ret.send(20)) # send 1.传一个值过去 2.让当前yield继续执行
print(ret.send(30))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
应用场景4:
生成器监听文件例子
import time
def tail(filename):
f = open(filename)
f.seek(0, 2) #从文件末尾算起
while True:
line = f.readline() # 读取文件中新的文本行
if not line:
time.sleep(0.1)
continue
yield line
tail_g = tail('tmp')
for line in tail_g:
print(line)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
yield与return的比较:
相同:都有返回值的功能
不同:return只能返回一次值,而yield可以返回多次值
def my_range(start,stop):
while True:
if start == stop:
raise StopIteration #raise会自动抛出异常
yield start
start+=1
g=my_range(1,3)
#
print(next(g))
print(next(g))
print(next(g))
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
yield的表达式应用
def eater(name):
print('%s 说:我开动啦' %name)
food_list=[]
while True:
food=yield food_list
food_list.append(food) #['骨头','菜汤']
print('%s eat %s' %(name,food))
alex_g=eater('alex')
#第一阶段:初始化
next(alex_g) #等同于alex_g.send(None)
print('===========>')
#第二阶段:给yield传值
print(alex_g.send('骨头')) #1 先给当前暂停位置的yield传骨头 2 继续往下执行,直到再次碰到yield,然后暂停并且把yield后的返回值当做本次调用的返回值
# print('===========>')
print(alex_g.send('菜汤'))
print(alex_g.send('狗肉包子'))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
通过两个函数之间交互传参:
def eater(name):
print('%s 说:我开动啦' %name)
food_list=[]
while True:
food=yield food_list
food_list.append(food) #['骨头','菜汤']
print('%s eat %s' %(name,food))
def producer():
alex_g=eater('alex')
#第一阶段:初始化
next(alex_g)
#第二阶段:给yield传值
while True:
food=input('>>: ').strip()
if not food:continue
print(alex_g.send(food))
producer()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
使用装饰器完成初始化:
#解决初始化问题
def init(func):
def wrapper(*args,**kwargs):
g=func(*args,**kwargs)
next(g) #初始化相当于next(alex_g)
return g
return wrapper
@init
def eater(name):
print('%s 说:我开动啦' %name)
food_list=[]
while True:
food=yield food_list
food_list.append(food) #['骨头','菜汤']
print('%s eat %s' %(name,food))
alex_g=eater('alex')
# 第二阶段:给yield传值
print(alex_g.send('骨头')) #1 先给当前暂停位置的yield传骨头 2 继续往下执行,直到再次碰到yield,然后暂停并且把yield后的返回值当做本次调用的返回值
print('===========>')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
上次更新: 2023/07/05, 16:57:02