迭代器

武功秘籍

 

可迭代对象

  迭代
    迭代即更新换代 每次的更新都必须依赖于上一次的结果

  '''迭代其实给我们提供了一种不依赖索引取值的方式'''

  可迭代对象
    内置有__iter__方法的都称之为可迭代对象
    内置的意思是可以通过点的方式直接查看到的
  """
  针对双下滑线开头 双下滑线结尾的方法 最为专业标准的读法为
  双下方法名

  面向对象的时候为了与隐藏变量区分开
  """

n = 1
while True:
    n+=1
    print(n)

l = [11,22,33,44,55,66]
n = 0
while n < len(l):
    print(l[n])
    n += 1

i = 12  # 没有
f = 11.11  # 没有
s = 'jason'  #
l = [111,22,33,4]  #
d = {'username':'jason','pwd':123}  #
t = (11,22,33)  #
se = {11,22,33}  #
b = True  # 没有
file = open(r'a.txt','w',encoding='utf8')
"""
含有__iter__的有
    字符串 列表 字典 元组 集合 文件对象
上述通常为可迭代对象
"""
print(d)
print(d.__iter__())  # 等价于调用了一个内置方法 d.get()
print(iter(d))
print(d.__len__())
print(len(d))

  可迭代对象调用__iter__方法会变成迭代器对象(老母猪)

  __iter__方法在调用的时候还有一个简便的写法iter()
  一般情况下所有的双下方法都会有一个与之对应的简化版本 方法名()

 

迭代器对象

  迭代器对象
    即含有__iter__方法 又含有__next__方法
  如何生成迭代器对象
    让可迭代对象执行__iter__方法

  文件对象本身即是可迭代对象又是迭代器对象
  迭代器对象无论执行多少次__iter__方法 还是迭代器对象(本身)

  迭代器给我们提供了不依赖于索引取值的方式

i = 12  # 没有
f = 11.11  # 没有
s = 'jason'  #
l = [111,222,333,444]  #
d = {'username':'jason','pwd':123}  #
t = (11,22,33)  #
se = {11,22,33}  #
b = True  # 没有
file = open(r'a.txt','w',encoding='utf8')

res = s.__iter__()  # 转成迭代器对象
print(res.__next__())  # 迭代器对象执行__next__方法其实就是在迭代取值(for循环)
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())


res = d.__iter__()  # 转成迭代器对象
print(res.__next__())  # 迭代器对象执行__next__方法其实就是在迭代取值(for循环)
print(res.__next__())
print(res.__next__())  # 取完元素之后再取会"报错"

# 易错
print(d.__iter__().__next__())  # username
print(d.__iter__().__next__())  # username
print(d.__iter__().__next__())  # username
print(d.__iter__().__next__())  # username
print(d.__iter__().__next__())  # username
print(d.__iter__().__next__())  # username

 

实例:for循环的本质

l1 = [1,2,3,4,5,6,7,8,9,11,22,33,44,55]
# 循环打印出列表中每个元素 但是不能使用for循环  __next__()  next()
# 1.先将列表转为迭代器对象
res = l1.__iter__()
# 2.循环执行__next__取值
while True:
    print(res.__next__())

for i in l1:
    print(i)

  for循环内部原理
    1.将关键字in后面的数据先调用__iter__方法转为迭代器对象
    2.循环执行__next__方法
    3.取完之后__next__会报错 但是for循环会自动捕获该错误并处理

  res = 数据.__iter__()
  while True:

    try:
      检测代码是否会报错
      res.__next__()

    except:

      pass
      如果报错了则自动处理掉并结束while循环

d = {'name':'jason','pwd':123,'hobby':'read'}

res = d.__iter__()  # StopIteration
while True:
    try:
        print(res.__next__())
    except StopIteration as e:
        break


for i in d:
    print(i)

 

迭代取值与索引取值对比

  

  迭代取值

    优点:
      不依赖于索引的一种通用取值方式
    缺点:
      取值的顺序永远都是固定的从左往右 无法重复获取  

  索引取值

    缺点:
      需要提供有序容器类型才可取值(不是一种通用的方式)
    优点:
      可以重复取值

 

 

生成器对象

  生成器其实就是自定义迭代器

# 定义阶段就是一个普通函数
def my_ge():
    print('first')
    yield 123,222,333
    print('second')
    # yield 456,444,555
"""
当函数体内含有yield关键字 那么在第一次调用函数的时候
并不会执行函数体代码 而是将函数变成了生成器(迭代器)
"""
# 调用函数:不执行函数体代码 而是转换为生成器(迭代器)
res = my_ge()
ret = res.__next__()  # 每执行一个__next__代码往下运行到yield停止 返回后面的数据
print(ret)
ret = res.__next__()  # 再次执行__next__接着上次停止的地方继续往后 遇到yield再停止
print(ret)

 

 

实例:自定义range功能

def my_range(start, stop=None, step=1):
    if not stop:
        stop = start
        start = 0
    while start < stop:
        yield start
        start += step
res = my_range(10)
for i in res:
    print(i)

res = my_range(1,10)
for i in res:
    print(i)

res = my_range(1,10,3)
for i in res:
    print(i)

 

yield传值(了解)

  通过.send(值)传给yield值

def eat(name):
    print('%s 准备干饭!!!'%name)
    while True:
        food = yield
        print('%s 正在吃 %s' % (name, food))
res = eat('jason')  # 并不会执行代码 而是转换成生成器
res.__next__()
res.send('肉包子')
res.send('盖浇饭')

 

yield与return对比

  yield

    1.可以返回值(支持多个并且组织成元组)
    2.函数体代码遇到yield不会结束而是"停住"
    3.yield可以将函数变成生成器 并且还支持外界传值

  return

    1.可以返回值(支持多个并且组织成元组)
    2.函数体代码遇到return直接结束

 

 

 

生成器表达式

  res = (i+1 for i in l)

l = [11, 22, 33, 44, 55, 66, 77, 88, 99]
res = [i+1 for i in l if i!=44]
print(res)

res1 = (i+1 for i in l if i!=44)
"""
生成器表达式内部的代码只有在迭代取值的时候才会执行
"""
print(res1.__next__())
print(res1.__next__())
print(res1.__next__())
"""
    迭代器对象 生成器对象 我们都可以看成是"工厂"
    只有当我们所要数据的时候工厂才会加工出"数据"
        上述方式就是为了节省空间
"""
# 求和
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)
    """
    第一次for循环
        g = (add(n, i) for i in g)
    第二次for循环
        g = (add(10, i) for i in (add(10, i) for i in g))
    """
res = list(g)
print(res)

#A. res=[10,11,12,13]
#B. res=[11,12,13,14]
#C. res=[20,21,22,23]
#D. res=[21,22,23,24]

 

 

END

 

posted @ 2021-11-19 15:46  Snails蜗牛  阅读(59)  评论(0)    收藏  举报