# 我们已经知道可以对list、tuple、dict、set、str等类型的数据使用for...in...的循环语法从其中依次拿到数据进行使用,
# 我们把这样的过程称为遍历,也叫迭代。
# 把可以通过for...in...这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象(Iterable)
"""
确切的定义:
1) 对象实现了__iter__() 方法
2) __iter__() 方法返回了一个迭代器对象
"""
#
# 如何验证对象是可迭代对象
from collections.abc import Iterable
print(isinstance([1, 2, 3], Iterable)) # True
print(isinstance(1, Iterable)) # False
# 迭代器是访问可迭代对象的工具
# 迭代器对象从第一个元素开始访问,知道所有的元素访问完才结束
# 迭代器只能往前读,不能后退
# 迭代器有两个函数:
# iter() 和 next() -> __iter__(), __next__()
# iter() 函数取得可迭代对象的迭代器。
"""
迭代器功能:
1. for 循环
2. 逐行遍历文本文件
3. 列表推导式
4. 元组拆包
特性:
1. 访问者不需要关心迭代器内部结构,只需要执行 next() 方法读取下一个内容
2. 不能随机访问集合中的某个值,只能从头到尾读取
3. 访问中途不能回退,不能访问之前的数据
4. 适合遍历很大的数据集合,节省内存,提升速度
5. 易耗损:迭代器经过一次依次取值的循环后就耗尽了,如果想要再次迭代需要重新构建迭代器
"""
# for i in 10:
# print(i) # TypeError: 'int' object is not iterable
#
# list1 = [1, 2, 3, 4]
# for i in list1:
# print(i)
# 1. for 循环原理
"""
1.通过__iter__()将可迭代对象转换为一个迭代器对象 -》 list1.__iter__()
2.调用迭代器对象中的__next__(),将迭代器中的元素一个一个按顺序输出 -》 (迭代器对象.__next__() 拿到一个返回值)
3.循环执行步骤2,知道抛出StopIteration的异常,捕捉到异常后结束循环
"""
list1 = [1,2,3,4]
list3 = iter(list1)
print(next(list3)) # 1
# list2 = list1.__iter__()
#
# try:
# print(list2.__next__())
#
# except StopIteration:
# print("iter over")
==================================================================================================
# 生成器是特定的迭代器
# "凭空"生成元素, 是一个一个生成,所以占用内存空间也只是一个
# 应用场景: 想得到庞大的数据,但是又想让它占用空间少,可使用生成器
# 关键字 yield - 一次返回一个结果,在每个结果中间,挂起函数,以便下次从它离开的地方继续执行
#
# def func():
# yield 1
#
#
# a = func()
# print(type(a))
# print(next(a)) # 1
# print(next(a)) # StopIteration
#
#
# def func():
# yield 1
# return "出现异常" # 它将是StopIteration的说明
#
#
# a = func()
# print(type(a))
# print(next(a)) # 1
# print(next(a)) # StopIteration: 出现异常
# def func(n):
# for i in range(n):
# yield i**2
#
#
# a = func(3)
# print(type(a))
# print(next(a)) # 0
# print(next(a)) # 1
# print(next(a)) # 4
# print(next(a)) # StopIteration
#
# def func():
# print("hello func")
# yield "a"
# yield "b"
# 调用方法1
# a = func()
# print(type(a))
# print(next(a)) # a
# print(next(a)) # b
# print(next(a)) # StopIteration
# 调用方法2
# for i in func():
# print(i) # a b
#
"""
创建生成器 -- 推导式
"""
# 列表推导式
# li = [i*2 for i in range(5)]
# print(li)
# 生成器表达式[]转换为()
#
# li2 = (i*2 for i in range(5))
# print(li2)
# print(next(li2))
# print(next(li2))
# print(next(li2))
# print(next(li2))
# print(next(li2))
# print(next(li2)) # StopIteration
# 生成器优点:当数据量或者计算量比较大时,时间和内存更优
import time
def exe_time():
s1 = time.time()
print(sum([i for i in range(100000000)]))
s2 = time.time()
print(f"the elapsed time {s2-s1}") # 10.479022741317749
def exe_time2():
s1 = time.time()
print(sum((i for i in range(100000000))))
s2 = time.time()
print(f"the elapsed time {s2-s1}") # 7.109840631484985
exe_time()
exe_time2()