一、迭代器
迭代协议:对象必须提供一个next方法,执行该方法后会返回迭代的下一项或者抛出Stopiteration异常,终止迭代。切只能往前,不能倒退。
可迭代对象:遵循迭代协议的对象就是可迭代对象。
迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next()。
字符串,列表或元组对象都可用于创建迭代器,生成迭代器的方法是iter():
1 li = [1,2,3,4] 2 #inter()本质调用了内置的__iter__ 3 a = li.__iter__() #生产一个可迭代对象 4 #next()本质调用__next__() 5 a.__next__() #对可迭代对象进行取值
方法二:
>>li = [1,2,3,4,5] >>it = iter(li) #生成一个迭代器 >>it.__next__() 1 >>next(it) #以上两种next都可以使用 2 >>for i in it: print(i) 3 4 5 #迭代完毕后,迭代器里面的数据就没有了 >> type(it) <class 'list_iterator'> #类型是迭代器类型
二、生成器
在描述生成器前我们先来了解列表生成式:
#普通定义的列表直接把列表写死 li = [1,2,3,4,5] #使用列表生成式,可以为列表添加一些新的属性 li1 = [a*a for a in range(10)] print(li1) #也可以用以下两种方法方法 li2 = list(map(lambda x:x+1,li)) print(li2) a = [] for i in range(10): a.append(i+1) print(a) #从以上对比可以看出,使用第二种方法代码最为简单,即列表生成式
虽然有列表生成式,可以简化生成特定列表的操作,但是当列表数据过大,就会过度的消耗内存,并且列表是数据也不会一直使用,在python中有一种一边循环一边计算的机制就是生成器。
生成器的第一种表现形式:
#创建一个生成器 li = (a*a for a in range(10)) print(li) #<generator object <genexpr> at 0x00000000027BD468>,返回一个生成器对象,用next调用对象 print(li.__next__()) print(next(li)) #且只能调用一次,不能往回调用 '''我们讲过,generator保存的是算法,每次调用next(g), 就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时, 抛出StopIteration的错误。一般情况下,我们用for循环来调用全部数据''' for i in li: print(i)
生成器的第二种表现形式:
生成器:一个函数调用时返回一个迭代器,那这个函数就叫做生成器(generator),如果函数中包含yield语法,那这个函数就会变成生成器 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。调用一个生成器函数,返回的是一个迭代器对象。
yield语法:每当行数运行到yield是就会中断并且保存当前所有的运行信息,并且在执行next()方法后继续从中断的地方运行。
#生成一个斐波拉契数列 def fib(max): a,b,n = 0,0,1 while n < max: a, b = b,a+b n +=1 return 'done' c = fib(5) print(c) #弊端,会直接全部生成 #把该函数变为一个生成器 def fib2(max): a,b,n = 0,0,1 while n < max: yield b #遇到一次返回一次,再次激活则运行后面的代码,用next激活 a, b = b,a+b n +=1 d = fib2(5) print(next(d))
下面是一个利用yield实现的一种单线程下的并发效果,也可以理解为一协程(后面会讲这一内容):
import time def consumer(name): print('%s准备吃包子了!' % name) while True: baozi = yield # 保存当前状态的值,并且退出函数 print('包子[%s]来了,被[%s]吃了!'% (baozi,name)) # c = consumer('alix') 生成一个生成器对象 # c.__next__() 调用生成器 def producer(): c = consumer('a') c2 = consumer('b') c.__next__() c2.__next__() print('老子开始准备做包子了') for i in range(10): time.sleep(1) print('做了1个包子,分两半') c.send(i) #给yield传值,并且唤醒yield c2.send(i) producer()
三、装饰器
装饰器:本质上就是函数,作用就是为其他函数添加其他功能
原则:1、不能修改被装饰的函数的源代码
2、不能修改被修饰函数的调用方式
在将装饰器前我们进行一些装饰器的知识储备:
1.函数即'变量'
2.高阶函数
1.把一个函数名当做实参传给另一个函数(添加功能不修改函数的源代码)
2.返回值中包含函数名(不修改函数的调用方式)
3.嵌套函数
函数即“变量”:
函数定义好后就像变量一样存储在内存中,当我们去调用的时候才会有意义。
#函数即变量 # def foo(): # print('in the foo') # foo() #即无需考虑函数的定义顺序,就和定义变量一样 def bar(): print('in the bar') def foo(): print('in the foo') bar() foo()
高阶函数:即把一个函数名当做实参传递给函数,就类似有把一个变量名传递给函数。
# 高阶函数:把一个函数名,当做实参传给函数 import time def bar(): time.sleep(3) #暂停3秒 print('in the bar') def text1(fun): start_time = time.time() #起始时间 fun() stop_time = time.time() #结束时间 print('the fun time is %s' % (stop_time-start_time)) #计算出fun()所用的时间 text1(bar) #将该函数名传递进去 def bar2(): time.sleep(3) print('in the bar2') def text2(fun): print(fun) return fun bar2 = text2(bar2) bar2()
通过高阶函数的作用可以看出:在不改变函数的源代码的情况下,给函数增加了计时的功能
嵌套函数:
def foo(): print('in the foo') def bar(): print('in the bar') bar() foo()
结合以上三种实现装饰器:
import time def timer(func): #timer(text1) func=text1 装饰器函数 def wrapper(*args,**kwargs): start_time = time.time() func() #run text1 stop_time = time.time() print('the fun run time is %s' % (stop_time-start_time)) return wrapper @timer # text1 = timer(text1) 这一步就是把text1传入timer def text1(): time.sleep(1) print('the is text1') text1()
带参数的装饰器:
user,passwd = 'alex','1234' def auth(outh_type): print('auth func:',outh_type) def outh_wrapper(func): def wrapper(*args,**kwargs): print('wrapper func args:',*args,**kwargs) if outh_type == 'local': username = input('Username:'.strip()) password = input('Password:'.strip()) if user == username and passwd == password: print('