我的python之路【第四章】装饰器、生成器、迭代器
装饰器:在遵循下面两个原则的前提下为被修饰者添加新功能
必须遵循两个原则
1、一定不能修改源代码
2、不能修改调用方式
下面有个函数index
def index(msg): print('this is index ',msg) index('sasa')
我要为函数index加一个装饰器,获取程序执行的时间,但是不能修改源代码和函数的调用方式
import time def timer(func): def wrapper(*args,**kwargs): #'tom','xxxx'---> start_time=time.time() print(args) res=func(*args,**kwargs) #这里是传入函数执行 stop_time=time.time() print('run time is %s'%(stop_time-start_time)) return "123" ##加完装饰器的函数,返回值在这里定义 return wrapper @timer #index=timer(index) def index(msg): print('this is index ',msg) print(index('sasa'))
装饰器练习:
1、低级版,实现调用函数进行密码验证
def auth(func): def warrper(*args): name=input('input name: ') passwd=input('input passwd: ') if name=='alex' and passwd=='123': print('登录成功') func(*args) else: print('登录失败') return '' return warrper @auth def hello(msg): print("hello ",msg) hello('Alex')
2、中级版(有参装饰器),实现采用不同方式进行用户验证
def auth_deco(auth_type='file'): def auth(func): def warrper(*args,**kwargs): name=input('input name: ') passwd=input('input passwd: ') if auth_type=='file': if name=='alex' and passwd=='123': print('登录成功') func(*args,**kwargs) else: print('登录失败') elif auth_type=='ldap': print("用户验证ldap") func(*args,**kwargs) return warrper return auth auth_type = 'ldap' @auth_deco(auth_type) def hello(msg): print("hello ",msg) auth_type = 'ldap' hello('alex')
3、思考题:高级版,验证一次,不需要一直验证。
生成器:
a=[1,2,3,4,5,6,7,8,9,10]
1、在我们生成列表时,系统会帮我们开辟一块内存空间来存储数据。当列表无限大的时候,这个列表占用的内存空间也会随之增大。
下面我们进行一个操作:将列表a中所有的元素进行加1生成一个新的列表。
#常规做法:循环列表重新为他进行赋值操作 for index,i in enumerate(a): a[index]=i+1 print(a)
通过一种快捷的方法:列表生成式
# 列表生成式 a=[i+1 for i in a] print(a)
#将列表中大于5的全部进行幂运算 a=[1,2,3,4,5,6,7,8,9,10] a=[i*i if i >5 else i for i in a] print(a)
以上的示范,你会发现这个列表a已经生成,此时它就会占用内存空间,这些有规律的列表我们可不可以让他在调用的时候才生成呢?
#列表生成器 #边执行边运算=惰性运算 a=(i*i if i >5 else i for i in a) print(a) #输出:<generator object <genexpr> at 0x000001D369B769E8>
将列表生成式的[]改成了(),此时的a变成了一个生成器。
生成器的特点
#提供next方法,来实现进行调用。编执行边运算,不知道下一个,上一个。只能一步步往下走。不知道尽头在哪儿里 #优点,不需要提前生成大量的数据来占用内存。节省内存。
#next() 和 a.__next__() 完全一样 print(next(a)) print(next(a)) print(next(a)) print(a.__next__())
#上面的next()方法太麻烦了。 #我们可以循环这个生成器来读取里面的内容 for i in a : print(i)
斐波那契数列:除了第一个和第二个数,任意一个数都可由前两个数相加得到:
1、1、2、3、5、8、13、21、34、……
让我们写一个函数实习这样的功能:
def fib(max): n, a, b = 0, 0, 1 while n < max: #max循环的次数 print(b) #t=a+b #a=b #b=t a, b = b, a + b n = n + 1 return 'done' f=fib(15)
输出:1 2 3 5 8 13 21 34 55 89 144 233 377 610
#将函数转换为生成器,yield def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done'
#此时yield 已经将函数变成了生成器 f=fib(10) print(f) #<generator object fib at 0x000001A0D70D6A40>
#在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。 print(f.__next__()) print(f.__next__()) print(f.__next__()) print(f.__next__()) print("可以在函数执行过程中做点别的事情") print(f.__next__()) print(f.__next__()) print(f.__next__())
#循环读取生成器,此时生成器内的元素并没有占用内存空间 for i in fib(100): print(i)
通过yield实现在单线程的情况下实现并发运算的效果:生成者消费者模型
#通过yield实现在单线程的情况下实现并发运算的效果 import time def xiaofeizhe(name): print("%s准备吃包子"%name) while True: baozi=yield print("来了第%s个包子, 被%s吃了"%(baozi,name)) def chushi(name): print("%s 开始做饭了"%name) x1=xiaofeizhe("贺磊") x2=xiaofeizhe("磊磊") x1.__next__() x2.__next__() for i in range(10): print("第%s个包子来了"%i) time.sleep(1) x1.send(i) x2.send(i) chushi("浩哥")
迭代器:
-
''' 一类是集合数据类型,如list、tuple、dict、set、str等; 一类是generator,包括生成器和带yield的generator function。 这些可以直接作用于for循环的对象统称为可迭代对象:Iterable 可以使用isinstance()判断一个对象是否是Iterable对象: ''' from collections import Iterable print(isinstance([], Iterable)) print(isinstance({}, Iterable)) print(isinstance('abc', Iterable)) print(isinstance((x for x in range(10)), Iterable)) print(isinstance(100, Iterable)) ''' 迭代器:*可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator 生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。 可以使用isinstance()判断一个对象是否是Iterator对象 ''' from collections import Iterator print(isinstance((x for x in range(10)), Iterator)) print(isinstance([], Iterator)) print(isinstance({}, Iterator)) print(isinstance('abc', Iterator)) '''把list、dict、str等Iterable变成Iterator可以使用iter()函数''' print(isinstance(iter([]), Iterator)) print(isinstance(iter([]), Iterator))