迭代
每一次
对过程的重复
称为一次“迭代”,而每一次迭代得到的结果
会作为下一次迭代的初始值
。例如:循环获取容器中的元素。
可迭代对象iterable
具有__iter__函数的对象,可以返回迭代器对象。
对list、tuple、dict、set、str等类型的数据可以通过for…in…这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象(Iterable)。
class 可迭代对象名称:
def __iter__(self):
return 迭代器
简单总结:
迭代器 = 可迭代对象.iter()
可以被next()函数调用并返回下一个值的对象
迭代器对象iterator
可以被next()函数调用并返回下一个值的对象。
实现了__next__()魔法方法(类实例化),该方法返回迭代器下一个值(保存得到下一个迭代值的算法)
class 迭代器类名:
def __init__(self, 聚合对象):
self.聚合对象= 聚合对象
def __next__(self):
if 没有元素:
raise StopIteration
return 聚合对象元素
理解可迭代对象iterable 与迭代器对象iterator
可迭代对象包含迭代器。
如果一个对象拥有__iter__方法,其是可迭代对象;如果一个对象拥有__next__方法,其是迭代器。
定义可迭代对象,必须实现__iter__方法;定义迭代器,必须实现__iter__和__next__方法。
使用__iter__与__next__实现迭代(不使用for):
# 定义list类型的可迭代对象
my_list = [1, 2, 3, 7, 8, 9]
# 获取迭代器
# iterator = my_list.__iter__() # 直接写法
# iter()函数实际上就是调用了可迭代对象的__iter__方法
iterator = iter(my_list)
# 循环获取下一个元素
while True:
try:
# item = iterator.__next__() 直接写法
item = next(iterator) # 与iter() 同理
print(item)
# 异常处理
except StopIteration: # 迭代完成
break
使用函数定义__iter__与__next__实现迭代:
class MyIterator:
def __init__(self, target):
self.target = target
# 下标计数器
self.index = 0
def __next__(self):
# 如果 下标计数器 > 可迭代对象长度
if self.index > len(self.target) - 1:
# 报错
raise StopIteration()
result = self.target[self.index]
self.index += 1
return result
class MyManager:
"""
可迭代对象
"""
def __init__(self):
# 定义一个函数私有变量__list 类型为list
self.__list = []
def add_obj(self, obj):
self.__list.append(obj)
def __iter__(self):
# 返回MyIterator类 实例化对象
return MyIterator(self.__list)
class Test:
def __str__(self):
return "这是Test类"
# 实例化MyManager类
manager = MyManager()
manager.add_obj(Test())
manager.add_obj(Test())
manager.add_obj(Test())
iterator = manager.__iter__()
while True:
try:
item = iterator.__next__()
print(item)
except StopIteration:
break
生成器generator
能够动态(循环一次计算一次返回一次)提供数据的可迭代对象。
作用:在循环过程中,按照某种算法推算数据,不必创建容器存储完整的结果,从而节省内存空间。数据量越大,优势越明显。在Python中,这种一边循环一边计算的机制,称为生成器:generator
ps: 当数据量足够大时,内存是完全不够用的,但是又需要迭代计算时,需要惰性操作 : 执行一次,计算一次,而不是创建容器,来存储整个对象
生成器函数
含有yield语句的函数,返回值为生成器对象。
yield翻译为”产生”或”生成”
调用生成器函数将返回一个生成器对象,不执行函数体。
# 创建生成器函数
def 函数名A():
…
yield 数据
# 使用生成器函数
for i in 函数名A():
语句
举例来说:
def Test():
print('Test1')
yield 'Test - Test1'
print('Test2')
yield 'Test - Test2'
print('Test3')
yield 'Test - Test3'
t = Test()
result = next(t)
print(result)
result = next(t)
print(result)
result = next(t)
print(result)
result = next(t) # 第四次调用 超出迭代范围
print(result)
Test1
Test - Test1
Test2
Test - Test2
Test3
Test - Test3
Traceback (most recent call last):
File "my_test_2.py", line 19, in <module>
result = next(t)
StopIteration
执行过程:
Setp1 - 调用生成器函数会自动创建迭代器对象。
Setp2 - 调用迭代器对象的__next__()方法时才执行生成器函数。
Setp3 - 每次执行到yield语句时返回数据,暂时离开。
Setp4 - 待下次调用__next__()方法时继续从离开处继续执行。
优点: 不会将所有结果计算出来,存储在内存中
缺点: 无法通过索引,切片灵活的访问结果
- 将
延迟操作转为立即操作 list() 函数
从而克服缺点
注意:
生成器只可for遍历一次,第二次遍历无效,需要重新调用生成器函数产生生成器
def test(l : list):
for i in l:
yield i
if __name__ == '__main__':
result = test([1, 2, 3])
for i in result:
print(i)
for i in result:
print(i)
# 结果 只调用一次:
1
2
3
使用 yeild 改写MyManager 实现惰性操作
# class MyIterator: 全部注释掉
#
# def __init__(self, target):
# self.target = target
# # 下标计数器
# self.index = 0
#
# def __next__(self):
# # 如果 下标计数器 > 可迭代对象长度
# if self.index > len(self.target) - 1:
# # 报错
# raise StopIteration()
#
# result = self.target[self.index]
# self.index += 1
# return result
class MyManager:
"""
可迭代对象
"""
def __init__(self):
# 定义一个函数私有变量__list 类型为list
self.__list = []
def add_obj(self, obj):
self.__list.append(obj)
def __iter__(self):
# return MyIterator(self.__list)
for item in self.__list:
# 将函数变为 生成器函数
yield item
class Test:
def __str__(self):
return "这是Test类"
# 实例化MyManager类
manager = MyManager()
manager.add_obj(Test())
manager.add_obj(Test())
manager.add_obj(Test())
iterator = manager.__iter__()
while True:
try:
item = iterator.__next__()
print(item)
except StopIteration:
break
lambda表达式
通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。
作用:作为参数传递时语法简洁,优雅,代码可读性强。
定义方法:
变量 = lambda 形参: 方法体
– 形参没有可以不填
– 方法体只能有一条语句,且不支持赋值语句。
# 普通函数
def condition1(item):
return item // 2
# lambda 匿名函数
num = lambda target: target // 2
if __name__ == '__main__':
print(condition1(100)) # 50
print(num(100)) # 50
相对普通方法的优缺点:
优点:
省略方法名
减少程序间依赖性
将方法作为参数,特别优雅
缺点:
由于没有名字,代码不能复用
方法体只能一条语句 错误方法: a = lambda item:print(“ok”) ; item % 2 == 0
方法体必须有返回值
Python之禅中有这么一句话:Explicit is better than implicit(明了胜于晦涩),就是说那种方式更清晰就用哪一种方式,不要盲目的都使用lambda表达式。