今日内容:
*有参装饰器
* 迭代器
* 生成器(自定义了一种迭代器)
一.有参装饰器
# 前面写的验证用户登陆的装饰器现在验证的数据来自于不同的地方 文件,数据库,idap等
def login(engine='file'): #engine='mysql'
def auth(func): #func=最原始那个index的内存地址
def wrapper(*args,**kwargs):
if engine == 'file':
inp_user = input('please input your username: ').strip()
inp_pwd = input('please input your password: ').strip()
if inp_user == 'egon' and inp_pwd == '123':
print('login successfull')
current_user['username']=inp_user # 在登陆成功之后立刻记录登录状态
res=func(*args,**kwargs) # res=最原始那个index的内存地址(*args,**kwargs)
return res
else:
print('username or password error')
elif engine == 'mysql':
print('基于mysql的认证机制')
elif engine == 'ldap':
print('基于ldap的认证机制')
else:
print('无法识别的认证源')
return wrapper
return auth
@login('file') #@auth # index=auth(最原始那个index的内存地址) #index=wrapper
def index():
print('welcome to index page')
time.sleep(3)
@login('file')
def home(name):
print('welcome %s to home page' %name)
time.sleep(2)
return 123
index()
home('egon')
补充
def outter1(func1): #func1=wrapper2的内存地址
print('加载了outter1')
def wrapper1(*args,**kwargs):
print('执行了wrapper1')
res1=func1(*args,**kwargs)
return res1
return wrapper1
def outter2(func2): #func2=wrapper3的内存地址
print('加载了outter2')
def wrapper2(*args,**kwargs):
print('执行了wrapper2')
res2=func2(*args,**kwargs)
return res2
return wrapper2
def outter3(func3): # func3=最原始的那个index的内存地址
print('加载了outter3')
def wrapper3(*args,**kwargs):
print('执行了wrapper3')
res3=func3(*args,**kwargs)
return res3
return wrapper3
@outter1 # outter1(wrapper2的内存地址)======>index=wrapper1的内存地址
@outter2 # outter2(wrapper3的内存地址)======>wrapper2的内存地址
@outter3 # outter3(最原始的那个index的内存地址)===>wrapper3的内存地址
def index():
print('from index')
print('======================================================')
index()
总结+补充
def index():
"""index注释"""
print('index')
print(index.__name__) # 查看函数字符串名
print(help(index)) # 查看函数的注释
from functools import wraps
# 有参装饰器的模板 最多就三层!
def outter1(x,y,z):
def outter2(func):
@wraps(func) # 真正做到以假乱真
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
return outter2
# 无参装饰器的模板
def outter(func):
@wraps(func) # 真正做到以假乱真
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
迭代器
"""
1 什么是迭代器
迭代指的是更新换代,是一个重复的过程但是每一次更新换代都需要基于上一次的结果之上
迭代器指的就是迭代取值的工具
2 为什么要用迭代器
迭代器提供了一种不依赖于索引取值的方式
3 如何使用迭代器
"""
# 错误示范
i = 0
while True:
print(i)
# 迭代:重复+每次重复都是基于上一次的结果基础之上
# 正确示范
l = ['a','b','c']
i = 0
while i < len(l):
print(l[i])
i += 1
"""
哪些是需要迭代取值 字符串,列表,元祖,字典,集合
但是字典和集合是无序的无法依赖于索引取值,那该如何迭代获取字典 集合这些无序容器内中的元素呢
这就需要用到迭代器 >>> 不依赖于索引完成取值操作
python给我们提供了内置的迭代器,但是在介绍这个之前需要先介绍可迭代对象的概念
"""
可迭代对象
"""
可迭代对象iterable:但凡内置有__iter__方法的对象都称之为可迭代的对象
列举所有所学的数据类型发现:str,list,tuple,dict,set,文件对象
"""
# 既然是内置方法,那么应该可以加括号调用执行
# 执行可迭代对象的__iter__方法,返回的值就是一个迭代器对象iterator
dic = {'name':'jason','age':18,'password':'123'}
iter_dic = dic.__iter__()
print(iter_dic) # 打印得到迭代器对象内存地址
# 迭代器对象如何不按照索引取值呢,迭代器对象下面都有一个__next__方法
迭代器对象
"""
1.什么是迭代器对象
迭代器对象完整描述:即内置有__next__方法也内置有__iter__方法
"""
dic = {'name':'jason','age':18,'password':'123'}
iter_dic = dic.__iter__()
print(iter_dic) # 打印得到迭代器对象内存地址
print(iter_dic.__next__())
print(iter_dic.__next__())
print(iter_dic.__next__())
print(iter_dic.__next__()) # 报错StopIteration是一种结束信号,标示无值可取
# 列表
l = ['a','b','c'] # 列表比较特殊 它可以不依赖于索引也支持索引取值
iter_l = l.__iter__()
print(iter_l.__next__())
print(iter_l.__next__())
print(iter_l.__next__())
# 文件
f = open('a.txt','r',encoding='utf-8')
iter_f = f.__iter__() # 由可迭代对象变成迭代器对象
print(iter_f.__next__())
print(iter_f.__next__())
print(iter_f.__next__())
# 疑问点
# 1
l = ['a','b','c']
print(l.__iter__().__next__()) # a
print(l.__iter__().__next__()) # a
print(l.__iter__().__next__()) # a
# 2
# 迭代器对象完整描述:即内置有__next__方法也内置有__iter__方法
# 也就以为着迭代器对象肯定是可迭代对象,而可迭代对象不一定是迭代器对象
# 再来验证所有的数据类型 >>> 文件对象即是可迭代对象也是迭代器对象
l = ['a','b','c','d']
iter_l = l.__iter__()
print(iter_l is iter_l.__iter__().__iter__().__iter__().__iter__().__iter__()) # True
# ps:迭代器对象调用多少次__iter__方法得到的还是迭代器对象本身
# 补充
iter(l) # 等价于l.__iter__()
len(l) # 等价于l.__len__()
dic = {'name':'jason','password':'123','hobby':'read'}
iter_dic = iter(dic)
while True:
print(next(iter_dic)) # iter_dic.__next__() 能够实现迭代取值但是取到末尾会报错
# 异常捕获初识(了解)
dic = {'name':'jason','password':'123','hobby':'read'}
iter_dic = iter(dic)
while True:
try:
print(next(iter_dic))
except StopIteration:
break
# 上述方法实现了不依赖于索引迭代取值的方式
for循环原理
"""
上面我们确实实现了不依赖于索引取值的操作,但是需要我们手动生成迭代器对象,手动while循环,加异常捕获。太过麻烦
有没有一种方式能够帮我们自动实现上面的几个步骤呢 >>> for循环
"""
dic = {'name':'jason','password':'123','hobby':'read'}
for i in dic:
print(i)
# for循环工作原理
"""
1.将in后面的对象调用__iter__方法,将其变为迭代器对象
2.调用next迭代取值,将得到的返回值赋值给变量名i
3.循环往复直到next抛出异常,for会自动捕获然后结束循环
"""
# 正是由于迭代器对象调用__iter__还是迭代器对象本身,所以这里可以统一先调用__iter__方法
总结
"""
再来看昨天学的几个重要的内置函数显示的默认参数,都有iterable形参
优点:
1.提供了一种不依赖于索引的迭代取值的方式
2.同一时刻在内存中只存在一个值,节省内存
python2与python3对比,节省内存的现象
map
range
...
缺点:
1.不能取指定的某一个值,只能从前往后一次取
2.无法明确迭代器内部数据的总长度
"""
生成器
自定义迭代器
"""
生成器就是一种自定义的迭代器,本质就是迭代器
上面都是用创建好的迭代器,现在学如何自己造
"""
def func():
print('first')
yield
# 但凡函数内包含yield关键字,调用函数不会执行函数体代码,而是会得到一个返回值,该返回值就是生成器对象
g = func() # 必须先调用一下函数才能拿到生成器对象
print(g)
# yield后也可以有返回值
def func():
print('first')
yield 1
print('secode')
yield 2
print('third')
yield 3
print('fourth')
g = func()
print(g) # 有__iter__,__next__
res1 = next(g) # next会触发函数执行,直到碰到一个yield停下来,并且yield后的值会当作本次返回值返回
print(res1)
res2 = next(g)
print(res2) # 基于上次暂停的位置继续往后执行直到遇到yield关键字停下并将yield后的值返回
res3 = next(g)
print(res3)
# 自定义range功能
def my_range(start,stop,step=1):
while start < stop:
yield start
start += step
obj = my_range(1,8,2) # 生成一个生成器对象
print(next(obj))
print(next(obj))
print(next(obj))
for i in my_range(1,9,2):
print(i)
yield表达式形式应用(了解,可不讲)
# 了解(*):yield的表达式形式的应用: x=yield
def dog(name):
print('狗哥 %s 准备开吃' %name)
food_list=[]
while True:
food=yield food_list # food = yield = '肉包子'
print('%s 吃了 %s' %(name,food))
food_list.append(food)
g=dog('alex')
# 强调:针对表达式形式的yield的使用,第一步必须让函数先暂停到一个yield的位置,才能进行传值操作
# next(g) # 张开狗嘴,让生成器先暂停到yield的位置,准备接收外部传进来的值
res1=next(g) # g.send(None) # 这一步必须有,不然直接报错
print(res1)
res2=g.send('肉包子') # 1. 先为当前暂停位置的yield赋值 2. next(生成器)直到再次碰到一个yield停下来
print(res2)
yield关键字总结
# 总结yield:只能在函数内使用
#1. yield提供了一种自定义迭代器的解决方案
#2. yield可以保存函数的暂停的状态
#3. yield对比return
# 1. 相同点:都可以返回值,值的类型与个数都没有限制
# 2. 不同点:yield可以返回多次值,而return只能返回一次值函数就结束了
生成器表达式
# 列表推导式(占内存)
l = [ i**2 for i in range(8) if i > 3 ]
print(l)
# 生成器表达式(当你需要产生一个比较大的容器对象的时候可以考虑使用生成器表达式)
g = ( i**2 for i in range(8) if i > 3 )
print(next(g))
print(next(g))
print(next(g))
# 统计文本文件中字符的个数
# 1.莽夫版本
with open(r'a.py','r',encoding='utf-8') as f:
data=f.read() # 文件太大的话不合适
print(len(data))
with open(r'D:\脱产三期视频\day14\01 生成器.py','rt',encoding='utf-8') as f:
# 2.原始的方式
# res=0
# for line in f:
# res+=len(line)
# print(res)
# 3.利用生成器表达式
# g=(len(line) for line in f)
# print(sum(g))
# 4.双括号可简写
res1=sum(len(line) for line in f)
print(res1)
# print(sum([1,2,3,4]))
面试题
def add(n,i):
return n+i
def test():
for i in range(4):
yield i
g=test()
for n in [1,10]:
g=(add(n,i) for i in g)
# 第一次循环:
# g=(add(10,i) for i in test())
# 第二次循环:
# g=(add(10, i) for i in (add(n,i) for i in test()))
# g其实就等于第二次循环的结果
res=list(g)
# 迭代器1=(add(10,i) for i in test())
# for i in 迭代器1: #i=next(迭代器1)
# add(n, i) #add(10,11)
#A. res=[10,11,12,13]
#B. res=[11,12,13,14]
#C. res=[20,21,22,23]
#D. res=[21,22,23,24]
# 答案是哪一个C