Python基础(五)迭代器、生成器和装饰器

迭代器

迭代器协议

迭代定义:html

迭代是重复反馈过程的活动,其目的一般是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代获得的结果会做为下一次迭代的初始值。python

 

1迭代器协议:对象必须提供一个__next__() 方法,执行该方法要么返回迭代中的下一项,要么就引发一个StopIteration异常,以终止迭代(只能往前,不能退后)编程

2可迭代对象(迭代器):实现了迭代器协议的对象(如何实现迭代器协议,在对象内部定义__iter__()方法微信

3协议是一种约定,可迭代对象实现了迭代器协议,Python的内部函数工具(如for循环,sum,min,max函数等)都是使用迭代器协议访问对象!!!闭包

 

迭代器做用:能够节省内存并发

 

迭代器相关的方法:iter() 和 next()app

python内置函数 next() 本质就是在调用__next__()ide

>>> list=[1,2,3,4]
>>> it = iter(list)    # 建立迭代器对象
>>> print (next(it))   # 输出迭代器的下一个元素
1
>>> print (next(it))
2
>>>

经过iter()还能够指定迭代至结束时的对象函数

l = ['a', 'b', 'c', 'd'] def test(): return l.pop() x = iter(test, 'b') print(x.__next__()) #d
print(x.__next__()) #c
print(x.__next__()) #到'b'了,抛异常

 

python中的for机制

(字符串str,列表list,元组tunple,字典dict,,集合set,文件对象)这些都不是可迭代对象,只不过在for循环,调用了他们的内部的__iter__()方法,把他们变成了可迭代对象,(补充:为何str,list,tunple能够有下标,例如list[0],由于他们是有序的)工具

for循环把他们变成了可迭代对象后,就调用可迭代对象的_next()_方法去取值,并且for循环会捕获StopIteration异常,以终止迭代!!!

1 name = 'ales'
2 print(n) 3 
4 输出结果 5 <str_iterator object at 0x00E7EF90> 

for循环就是基于迭代器协议提供一个统一的能够遍历全部对象的方法,即在遍历以前,先调用对象的__iter__()方法将其转换成一个迭代器,而后使用迭代器协议去实现循环访问。这样全部的对象均可以经过for循环来遍历。name = 'ales'n = name.__iter__() #遵循迭代协议,调用_iter_方法生成可迭代对象

print(n.__next__())
print(n.__next__())
print(n.__next__())
print(n.__next__())
print(n.__next__())# 迭代至StopIteration异常

输出结果
a
l
e
s
Traceback (most recent call last):
  File "E:/PycharmProjects/untitled/day18/迭代器.py", line 9, in <module>
    print(n.__next__())
StopIteration

 

 利用while模仿for循环

1 list1 = [1, 2, 3, 4] 2 l = list1.__iter__() 3 while True: 4     try: 5         print(l.__next__()) 6     except StopIteration: 7         break
执行结果

1
2
3
4 

生成器

什么是生成器

能够理解为一种数据类型,这种数据类型自动实现了迭代器协议,其余的数据类型须要须要调用本身内置的__iter__方法,因此生成器就是可迭代对象

 

python中有两种方式提供生成器:

1.生成器函数:常规函数定义,可是使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每一个结果中,挂起函数的状态,以便下次从它离开的地方继续执行

2.生成器表达式:相似于列表解析,可是生成器返回按需产生结果的一个对象,而不是一次构建出的结果列表

生成器函数

def test():
    yield 1
    yield 2
    yield 3

x = test()
print(x)
print(x.__next__())
print(x.__next__())
print(x.__next__())
print(x.__next__())


执行结果
<generator object test at 0x00C834F0>
1
2
3
    print(x.__next__())
StopIteration 

生成器函数和普通函数

普通函数

# 普通函数
def test():
    print('start')

test()

运行结果
start

生成器函数

# 生成器函数是一个可迭代对象,注意与普通函数的区别
def test():
    print('start')
    yield '这是第一个yield语句的返回值'
    print('这是第一个yield的下一行')
    
    yield '这是第二个yield语句的返回值'
    print('这是第二个yield的下一行')
    
    yield '这是第三个yield语句的返回值'
    print('这是第三个yield的下一行')

a = test() #生成一个可迭代对象 
print(a) 
print(type(a)) 

运行结果 
<generator object test at 0x02DF35B0> 
<class 'generator'>

  接上

第一种

a.__next__() #运行至第一个yield语句结束,并保留运行状态
a.__next__() #从上次的运行状态开始,运行至第二个yield语句结束
a.__next__()

运行结果
start
这是第一个yield的下一行
这是第二个yield的下一行 

第二种

res = a.__next__() ##运行至第一个yield语句结束,保留运行状态,并将yield的返回值赋值给res
print(res)
运行结果
start
1 

第三种

res = a.__next__() #运行至第一个yield语句结束,保留运行状态,并将yield的返回值赋值给res
print(res)
a.__next__() #从上次的运行状态开始,运行至第二个yield语句结束

运行结果
start
这是第一个yield语句的返回值
这是第一个yield的下一行

生成器表达式

num = ('num%d' %i for i in range(10))#生成器表达式
print(num)
print((num.__next__()))
print((num.__next__()))
print(next(num))
print(next(num))

运行结果
<generator object <genexpr> at 0x00AA34F0>
num0
num1
num2
num3

 列表解析和生成器表达式

>>> l = [i for i in range(0,15)] #列表解析
>>> print(l)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> m = (i for i in range(0,15)) #生成器表达式
>>> print(m)
<generator object <genexpr> at 0x104b6f258>
>>> for g in m:
...     print(g,end=', ')
... 
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,

把列表解析的 [ ] 换成 ( ) 获得的就是生成器表达式

列表解析和生成器表达式都是遍历的编程方式,可是生成器表达式更节省内存

# 列表解析
print([i for i in range(1,1000000)]) #内存占用过大,机器易卡死

# 生成器表达式
m = (i for i in range(1,100000000)) #几乎不占内存,运行的时间取决于cpu
print(sum(m))

python大部份内置函数都是使用迭代器协议访问对象。如 sum()

sum(x**2 for x in range(4))

sum([x ** 2 for x in range(4)]) #不用画蛇添足构造列表

__next__()

__next__()在生成器函数中能够运行至函数的下一个yield语句结束,并保留运行状态,返回yield语句定义的返回值

next()

next() 返回迭代器的下一个项目。

内置函数 next() 本质就是在调用__next__()

生成器的send用法

def test():
    print('start')
    first = yield 1
    print('这是第一个yield的下一行', first)
    second = yield 2
    print('这是第二个yield的下一行', second)
    yield


t = test() #产生生成器
print(t.__next__()) #第一次运行只能使用next或者send(None)
t.send('this is the first send value') #send具备next的功能,让程序运行到下一个yield语句结束
t.send('this is the second send value')

  运行结果

start
1
这是第一个yield的下一行 this is the first send value
这是第二个yield的下一行 this is the second send value

Process finished with exit code 0

使用生成器实现并发

import time def eat_grass(name): print('我是%s,我准备开始吃牧草了' % name) while True: grass = yield time.sleep(1) print('我开心地吃了第%s棵牧草' %grass) # name = 'cow' # eg = eat_grass(name) # eg.__next__() # eg.send('grass_number_01') # eg.send('grass_number_02') # eg.send('grass_number_03')

def product_grass(): name = 'cow' eg = eat_grass(name) eg.__next__() for i in range(1,10): time.sleep(1) eg.send('%d' %i) product_grass()

 

总结

以生成器函数为例进行总结:

一、语法上和函数相似:差异在于常规函数只能使用一个return语句返回值,而生成器可使用多个yield语句返回值

二、自动实现迭代器协议:Python对于生成器自动实现了迭代器协议,因此能够调用 next,并在没有值能够next的时候,生成器自动产生StopIteration异常。还能够应用到迭代背景中(如for循环,sum函数)

三、状态挂起:生成器使用yield语句返回一个值,此时yield语句挂起该生成器函数的状态,保留足够信息,以便以后的使用都是基于最新的状态

 

优势:

一、生成器的好处时延迟计算,一次返回一个结果。这样能够节省内存,提升运行效率。对大数据处理很是有用。

见列表解析和生成器表达式的对比

二、生成器能够提供代码的可读性

注意:生成器只能遍历一次(例如生命只有一次,且年龄只会愈来愈大,不能返老还童)

杂货铺

列表解析

l = [i for i in range(10)]#列表解析
print(l)

# 等价于
l = []
for i in range(10):
    l.append(i)
print(l)

三元表达式

age = 20
res = 'adult' if age>20 else 'child' #三元表达式
print(res)

# 等价于
age = 20
res = ''
if age > 20:
    res = 'adult'
else:
    res = 'child'
print(res)

装饰器

概念和原则

装饰即修饰,意指为其余函数添加新功能;

装饰器的本质就是函数

做用是为其余函数添加新功能,如计算该函数运行时长

装饰器遵循原则:

1.不修改被装饰函数的源代码(开放封闭原则)

2.为被装饰函数添加新功能后,不能修改被修饰函数的调用方式

装饰器的实现 = 高阶函数 + 函数嵌套 + 闭包

高阶函数

高阶函数 = 函数接收的参数是一个函数名 或 函数返回值包含函数名

  参数是一个函数名,能够为被修饰函数添加新功能

  返回值包含函数,能够不改变被修饰函数的调用方式

 1 # 高阶函数:参数包含函数名;或者返回值包含函数名
 2 
 3 import time  4 
 5 def foo():  6     time.sleep(1)  7     print('this is from foo')  8 
 9 def deco(func): #参数是一个函数名,能够为被修饰函数添加新功能
10     starttime = time.time() 11  func() 12     endtime = time.time() 13     print('运行%s函数共耗时%f' %(func,endtime-starttime)) 14     return func #返回值包含函数,能够不改变被修饰函数的调用方式
15 
16 foo = deco(foo) 17 foo() 18 # 缺陷:这里被修饰函数运行了两次,因此生成器不能只靠高阶函数!

函数嵌套和闭包

函数嵌套:在函数体中定义函数

闭包:闭包就是可以读取其余函数内部变量的函数。在一个做用域里放入定义变量,至关于打了一个包

def country(address): # name = 'alex'
    print('this is from country') def province(): # name = 'blice'
        print('this is from province %s' %address) country('个人地址')

装饰器的使用

 1 import time  2 
 3 def deco(func):  4     def qiantao():  5         starttime = time.time()  6  func()  7         endtime = time.time()  8         print('运行%s函数共耗时%f' %(func,endtime-starttime))  9     return qiantao 10 
11 
12 def foo(): 13     time.sleep(1) 14     print('this is from foo') 15 
16 
17 foo = deco(foo) #deco(foo)就是qiantao
18 foo() #这里foo()至关于qiantao()
不完善的装饰器
 1 import time  2 
 3 def deco(func):  4     def qiantao():  5         starttime = time.time()  6         res = func()  7         endtime = time.time()  8         print('运行%s函数共耗时%f' %(func,endtime-starttime))  9         return res 10     return qiantao 11 
12 
13 @deco #就至关于在调用foo()以前执行 foo = deco(foo)
14 def foo(): 15     time.sleep(1) 16     print('this is from foo') 17     return 'foo的返回值'
18 
19 
20 # foo = deco(foo)
21 res = foo() 22 print(res)
加上返回值和@
 1 import time  2 
 3 def deco(func):  4     def qiantao(*args, **kwargs): # args=(name, age) kwargs={gender:male,}
 5         starttime = time.time()  6         res = func(*args, **kwargs) #*(name,age) **{gender:male}
 7         endtime = time.time()  8         print('运行%s函数共耗时%f' %(func,endtime-starttime))  9         return res 10     return qiantao 11 
12 female = ''
13 @deco #就至关于在调用foo()以前执行 foo = deco(foo)
14 def foo(name, age, gender=female): 15     time.sleep(1) 16     print('this is from foo, =====> my name is %s ,age is %s, gender is %s' %(name, age,gender)) 17     return 'foo的返回值'
18 
19 res = foo('liming', 18, gender = '') 20 print(res)
修饰带参数的函数

应用实例:

 1 user_list = [{'name':'alex', 'pwd':'123'},  2             {'name':'belief', 'pwd':'123'},  3             {'name':'a', 'pwd':'123'},]  4 
 5 current_dic = {'username':'','login':False}  6 
 7 def auth_fun(fun):  8     def warp(*args, **kwargs):  9         if current_dic['username'] and current_dic['login']: 10             res = fun(*args, **kwargs) 11             return res 12         username = input('please input your name>') 13         password = input('please input your password>') 14         for user_dic in user_list: 15             if username==user_dic['name'] and password==user_dic['pwd']: 16                 current_dic['username'] = username 17                 current_dic['login'] = True 18                 res = fun(*args, **kwargs) 19                 return res 20         else: 21             print('用户名或密码错误') 22     return warp 23 
24 # index = auth_fun(index)
25 @auth_fun 26 def index(): 27     print('欢迎访问该网站') 28 
29 @auth_fun 30 def home(name): 31     print('这是的%s家页面' %name) 32 
33 @auth_fun 34 def shopping_car(name): 35     print('%s的购物车里有%s' %(name, '衣服')) 36 
37 index() 38 # home('belief')
39 # shopping_car('belief')
无参装饰器
 1 user_list = [{'name':'alex', 'pwd':'123'},  2             {'name':'belief', 'pwd':'123'},  3             {'name':'a', 'pwd':'123'},]  4 
 5 current_dic = {'username':'','login':False}  6 
 7 def auth(type='qq'):  8     '''根据用户的登陆方式来进行登陆操做'''
 9     def auth_fun(fun): 10         def warp(*args, **kwargs): 11             print('登陆方式是%s' %type) 12             if type=='qq': 13                 if current_dic['username'] and current_dic['login']: 14                     res = fun(*args, **kwargs) 15                     return res 16                 username = input('please input your name>') 17                 password = input('please input your password>') 18                 for user_dic in user_list: 19                     if username==user_dic['name'] and password==user_dic['pwd']: 20                         current_dic['username'] = username 21                         current_dic['login'] = True 22                         res = fun(*args, **kwargs) 23                         return res 24                 else: 25                     print('用户名或密码错误') 26             elif type=='wx': 27                 print('打开微信扫一扫') 28             else: 29                 print('不支持其余登陆方式,请使用qq或微信') 30         return warp 31     return auth_fun 32 
33 # index = auth_fun(index)
34 @auth(type='qq') #autn(type='qq')就是auth_fun @auth_fun至关于index=auth_fun(index), 即index = warp
35 def index(): 36     print('欢迎访问该网站') 37 
38 @auth(type='wx') 39 def home(name): 40     print('这是的%s家页面' %name) 41 
42 @auth(type='sj') 43 def shopping_car(name): 44     print('%s的购物车里有%s' %(name, '衣服')) 45 
46 # index()
47 home('belief') 48 # shopping_car('belief')
带参装饰器

 参考

https://www.runoob.com/python3/python3-iterator-generator.html

https://www.cnblogs.com/linhaifeng/articles/6140395.html

相关文章
相关标签/搜索