一、迭代器
-
-
可迭代对象:
字面意思:
-
对象:Python中一切皆对象,一个实实在在的值,对象
-
可迭代:更新迭代,类似重复的,循环的一个过程,但是每次都有新的内容,可以进行循环更新的一个实实在在的值
专业角度:
-
内部含有
'__iter__'
方法的对象,可迭代对象
目前学过的可迭代对象:str,list,tuple,dict,set,range,文件句柄
-
-
获取一个对象的所有方法:dir()
-
判断一个对象是否是可迭代对象
s1 = 'fjdskl'
print(dir(s1))
print('__iter__' in dir(s1)) # True
l1 = [1,2,3]
print(dir((l1)))
print('__iter__' in dir(l1)) # True
print('__iter__' in dir(range(10))) # True -
小结
-
字面意思:可以进行循环更新的一个实实在在值。
-
专业角度: 内部含有
'__iter__'
方法的对象,可迭代对象。 -
判断一个对象是不是可迭代对象:
'__iter__'
in dir(对象) -
str list tuple dict set range
-
优点:
-
存储的数据直接能显示,比较直观。
-
拥有的方法比较多,操作方便。
-
-
缺点:
-
占用内存。
-
不能直接通过for循环,不能直接取值(索引,key)。
-
-
-
-
迭代器定义:
-
字面意思:更新迭代,器:工具,可更新迭代的工具
-
专业角度:内部含有
'__iter__'
方法并且含有'__next__'
方法的对象就是迭代器 -
可以判断是否是迭代器:
'__iter__'
in dir(对象)and'__next__'
in dir(对象) -
判断一个对象是否是迭代器
with open('a1',encoding='utf-8',mode='w') as f1:
print( '__iter__' in dir(f1) and '__next__' in dir(f1)) -
迭代器的取值
s1 = 'fjdags'
obj = iter(s1) # s1.__iter__() #把可迭代对象转化成迭代器
# print(obj)
print(next(obj)) # print(obj.__next__()) # f
print(next(obj)) # print(obj.__next__()) # j
print(next(obj)) # print(obj.__next__()) # d
print(next(obj)) # print(obj.__next__()) # a
print(next(obj)) # print(obj.__next__()) # g
print(next(obj)) # print(obj.__next__()) # s
#可迭代对象有几个元素,就next几个值,多一个就会报错,next一次,取一个值
l1 = [11,22,33,44,55,66]
obj = iter(l1)
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj)) -
可迭代对象如何转化成迭代器
iter([1,2,3])
-
小结
-
字面意思:更新迭代,器:工具:可更新迭代的工具。
-
专业角度:内部含有
'__iter__'
方法并且含有'__next__'
方法的对象就是迭代器。 -
优点:
-
节省内存。
-
惰性机制,next一次,取一个值。
-
-
缺点:
-
速度慢。
-
不走回头路。
-
-
-
可迭代对象与迭代器的对比:
-
可迭代对象是一个操作方法比较多,比较直观,存储数据相对少(几百万个对象,8G内存是可以承受的)的一个数据集。
-
当你侧重于对于数据可以灵活处理,并且内存空间足够,将数据集设置为可迭代对象是明确的选择。
-
是一个非常节省内存,可以记录取值位置,可以直接通过循环+next方法取值,但是不直观,操作方法比较单一的数据集。
-
当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择。
-
-
-
while循环模拟for循环机制(要会默写,面试经常考)
l1 = [11,22,33,44,55,66,77,88,99,1111,1133,15652]
# 将可迭代对象转化成迭代器。
obj = iter(l1)
while 1:
try:
print(next(obj))
except StopIteration:
break
二、生成器
-
唯一的区别:生成器是我们自己用Python代码构建的数据类型,迭代器都是提供的,或者转化得来的
-
获取生成器的三种方式:
-
生成器函数
-
生成器表达式
-
Python内部提供的一些
-
-
-
生成器函数
-
yield
函数
def func():
print(111)
print(222)
return 3
ret = func()
print(ret)
生成器函数
def func():
print(111)
print(222)
yield 3
a = 1
b = 2
c = a + b
print(c)
yield 4
ret = func()
# print(ret) <generator object func at 0x000001FC4CF35A40>
print(next(ret))
print(next(ret))
一个next 对应一个yield -
yield return
return:函数中只存在一个return,结束函数,并且给函数的执行者返回值
yield:只要函数中有yield,那么它就是生成器函数而不是函数了。生成器函数中可以存在多个yield,yield不会结束生成器函数,一个yield对应一个next
-
吃包子练习题:
def func():
l1 = []
for i in range(1,5001):
l1.append(f'{i}号包子')
return l1
ret = func()
print(ret)
def gen_func():
for i in range(1,5001):
yield f'{i}号包子'
ret = gen_func()
# [3号包子.]
for i in range(200):
print(next(ret)) #可以取到1~200的包子
for i in range(50):
print(next(ret)) #从201~250的包子 -
yield from
def func():
l1 = [1, 2, 3, 4, 5]
yield l1
ret = func()
print(next(ret))
def func():
l1 = [1, 2, 3, 4, 5]
yield from l1
'''
yield 1
yield 2
yield 3
yield 4
yield 5
'''
将l1这个列表变成了迭代器返回。
ret = func()
print(next(ret))
print(next(ret))
print(next(ret))
print(next(ret))
print(next(ret)) -
简述一下yield 与yield from的区别。
yield 是生成器函数的标识,函数中只要有yield那么他就是生成器函数而不是函数。
next会从生成器函数中的yield获取值。
yield from 从便于理解的角度讲:它会将一个可迭代对象里面的每一个值作为生成器的生成的值:
yield from ['卫龙','老冰棍','北冰洋','牛羊配']
等同于:
yield '卫龙'
yield '老冰棍'
yield '北冰洋'
yield '牛羊配'
从更深层的角度去讲yield from有两个作用(一定要背过,面试题):
1. 他可以完全代替了内层循环,提高效率,让代码读起来更顺畅(下一道题就可以验证)。
2. 还可以创建通道,把内层生成器直接与外层生成器的客户端连接起来
-
-
生成器表达式,列表推导式
-
列表推导式
-
用一行代码构建一个比较复杂有规律的列表
-
列表推导式分两种:
-
循环模式:[变量(加工后的变量)for 变量 in iterable]
#1.将10以内所有证书的平方写入列表
l1 = [i*i for i in range(11) ]
print(l1)
#2.100以内所有的偶数写入列表
l2 = [i for i in range(101) if i % 2 == 0]
print(l2)
l3 = [i for i in range(0,101,2)]
print(l3)
#3.从python1期大Python100期写入列表lst
lst = [f'python{i}期' for i in range(1,101)]
print(lst)-
筛选模式:[变量(加工后的变量)for 变量 in iterable if 条件]
#1. 过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母
l1 = ['barry','ab','alex','wusir','xo']
l1 = [i.upper() for i in l1 if len(i) >= 3 ]
print(l1)
#2.含有两个'e'的所有人名留下来,并全部大写
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
#普通方法:
l1 = []
for i in names:
for j in i:
count = 0
if j.count('e') == 2:
l1.append(j.upper())
print(l1)
#列表推导式:
l2 = [j.upper() for i in names for j in i if j.count('e') == 2]
print(l2) -
-
-
-
生成器表达式:
与列表推导式的写法几乎一模一样,也有筛选功能,循环模式,多层循环构建。写法上只有一个不同:[]换成了()
print([i for i in range(1,11)])
print((i for i in range(1,11)))
obj = (i for i in range(1,11))
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
for i in obj:
print(i) -
总结:
-
列表推导式:
-
缺点:
-
列表推导式只能构建比较复杂并且有规律的列表
-
超过三层循环才能构建成功的,不建议用列表推导式
-
查找错误不行
-
优点:
-
一行构建,简单
-
装逼
-
-
列表推导式与生成器区别:
-
写法上: [] ()
-
iterable interator
-
-
-
-
字典推导式(了解)
lst1 = ['jay', 'jj', 'meet']
lst2 = ['周杰伦','林俊杰','元宝']
dic = { lst2[i]: lst1[i] for i in range(len(lst1))}
print(dic)
# {'周杰伦': 'jay', '林俊杰': 'jj', '元宝': 'meet'} -
集合推导式(了解)
print({i for i in range(1,11)})
# {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
三、装饰器
-
-
开放:对代码的拓展开放的
-
封闭:对源码的修改是封闭的
装饰器:装饰,装修,房子就可以住,如果装修,不影响你住,而且体验更加,让你生活中增加了很多功能:洗澡,看电视,沙发。
器:工具。
开放封闭原则:
开放:对代码的拓展开放的, 更新地图,加新枪,等等。
封闭:对源码的修改是封闭的。闪躲用q。就是一个功能,一个函数。 别人赤手空拳打你,用机枪扫你,扔雷.....这个功能不会改变。
装饰器:完全遵循开放封闭原则。
装饰器: 在不改变原函数的代码以及调用方式的前提下,为其增加新的功能。
装饰器就是一个函数。 -
-
装饰器的初识:
-
版本一:写一些代码测试一下index函数的执行效率
import time
def index():
'''有很多代码.....'''
time.sleep(2) # 模拟的网络延迟或者代码效率
print('欢迎登录博客园首页')
def dariy():
'''有很多代码.....'''
time.sleep(3) # 模拟的网络延迟或者代码效率
print('欢迎登录日记页面')
# print(time.time()) # 格林威治时间。
print(111)
time.sleep(3)
print(222)
# 版本一有问题: 如果测试别人的代码,必须重新赋值粘贴。
import time
def index():
'''有很多代码.....'''
time.sleep(2) # 模拟的网络延迟或者代码效率
print('欢迎登录博客园首页')
start_time = time.time()
index()
end_time = time.time()
print(end_time - start_time)
# 测试了第一个人的代码,如果再加一个人就需要复制粘贴了
import time
def index():
'''有很多代码.....'''
time.sleep(2) # 模拟的网络延迟或者代码效率
print('欢迎登录博客园首页')
def dariy():
'''有很多代码.....'''
time.sleep(3) # 模拟的网络延迟或者代码效率
print('欢迎登录日记页面')
start_time = time.time()
index()
end_time = time.time()
print(end_time - start_time)
start_time = time.time()
dariy()
end_time = time.time()
print(end_time - start_time) -
版本二:利用函数,解决代码重复使用的问题
import time
def index():
'''有很多代码.....'''
time.sleep(2) # 模拟的网络延迟或者代码效率
print('欢迎登录博客园首页')
def dariy():
'''有很多代码.....'''
time.sleep(3) # 模拟的网络延迟或者代码效率
print('欢迎登录日记页面')
def timmer(f): # f = index
start_time = time.time()
f() # index()
end_time = time.time()
print(f'测试本函数执行效率{end_time - start_time}')
timmer(index)
#版本二还是有问题: 原来index函数源码没有变化,给原函数添加了一个新的功能测试原函数的执行效率的功能。
#满足开放封闭原则么?原函数的调用方式改变了。 -
版本三:不能改变原函数的调用方式
import time
def index():
'''有很多代码.....'''
time.sleep(2) # 模拟的网络延迟或者代码效率
print('欢迎登录博客园首页')
def timmer(f): # f = index (funciton index123)
def inner():
start_time = time.time()
f() # index() (funciton index123)
end_time = time.time()
print(f'测试本函数执行效率{end_time - start_time}')
return inner # 把inner返回给调用者ret
ret = timmer(index) # 调用timmer函数 # 此时ret = inner
ret() # inner(),调用inner函数
-
版本四:具体研究
import time
def index():
'''有很多代码.....'''
time.sleep(2) # 模拟的网络延迟或者代码效率
print('欢迎登录博客园首页')
def timmer(f):
f = index
def inner():
start_time = time.time()
f()
end_time = time.time()
print(f'测试本函数执行效率{end_time - start_time}')
return inner
index = timmer(index)
index() -
版本五:python做了一个优化;提出了一个语法糖的概念。 标准版的装饰器
import time
def timmer(f):
def inner():
start_time = time.time()
f()
end_time = time.time()
print(f'测试本函数执行效率{end_time - start_time}')
return inner
-
版本六:被装饰函数带返回值
import time
# timmer装饰器
def timmer(f):
# f = index
def inner():
start_time = time.time()
# print(f'这是个f():{f()}!!!') # index()
r = f()
end_time = time.time()
print(f'测试本函数的执行效率{end_time-start_time}')
return r
return inner
-
版本七:被装饰函数带参数
import time
# timmer装饰器
def timmer(f):
# f = index
def inner(*args,**kwargs):
# 函数的定义:* 聚合 args = ('李舒淇',18)
start_time = time.time()
# print(f'这是个f():{f()}!!!') # index()
r = f(*args,**kwargs)
# 函数的执行:* 打散:f(*args) --> f(*('李舒淇',18)) --> f('李舒淇',18)
end_time = time.time()
print(f'测试本函数的执行效率{end_time-start_time}')
return r
return inner
标准版的装饰器;
def wrapper(f):
def inner(*args,**kwargs):
'''添加额外的功能:执行被装饰函数之前的操作'''
ret = f(*args,**kwargs)
''''添加额外的功能:执行被装饰函数之后的操作'''
return ret
return inner
-
-
装饰器的应用:登录认证
# 装饰器的应用:登录认证
# 这周的周末作业:模拟博客园登录的作业。装饰器的认证功能。
def login():
pass
def register():
pass
status_dict = {
'username': None,
'status': False,
}
def auth(f):
'''
你的装饰器完成:访问被装饰函数之前,写一个三次登录认证的功能。
登录成功:让其访问被装饰得函数,登录没有成功,不让访问。
:param f:
:return:
'''
def inner(*args,**kwargs):
'''访问函数之前的操作,功能'''
if status_dict['status']:
ret = f(*args,**kwargs)
'''访问函数之后的操作,功能'''
return ret
else:
username = input('请输入用户名').strip()
password = input('请输入密码').strip()
if username == 'taibai' and password == '123':
print('登录成功')
status_dict['username'] = username
status_dict['status'] = True
ret = f(*args, **kwargs)
return ret
else:
print('登录失败')
return inner