python 迭代器 生成器

迭代器 与 生成器

"""
什么是迭代器
    迭代:更新换代(重复)的过程,每次的迭代都必须基于上一次的结果
    迭代器:迭代取值的工具

为什么要用
    迭代器给你提供了一种不依赖于索引取值的方式

如何用
"""
 不算 因为只是简单的重复
 n = 0
 while True:
     print(n)


 重复 + 每次迭代都是基于上一次的结果而来的
 l = [1,2,3,4]
 s = 'hello'
 n = 0
 while n < len(s):
     print(s[n])
     n += 1
"""
需要迭代取值的数据类型
    字符串
    列表
    元组
    字典
    集合
"""

 可迭代对象
 只有内置有__iter__方法的都叫做可迭代对象
"""
补充:针对双下线开头双下划线结尾的方法
推荐读:双下+方法名

基本数据类型中
    是可迭代对象的有
        str list tuple dict set 
        文件对象(执行内置的__iter__之后还是本身 没有任何变化):文件对象本身就是迭代器对象
        
"""
n = 1
f = 1.1
s = 'hello'
l = [1,2,34,]
t = (1,2,34)
s1 = {1,2,3,4}
d = {'name':'jason'}
f1 = open('xxx.txt','w',encoding='utf-8')


 res = s.__iter__()   res = iter(s)

 print(s.__len__())   简化成了len(s)
 res1 = l.__iter__()   res1 = iter(l)
 res2 = f1.__iter__()   res2 = iter(f1)
 print(res,res1,res2)
 print(f1)

 可迭代对象执行内置的__iter__方法得到就是该对象的迭代器对象

"""
迭代器对象
    1.内置有__iter__方法
    2.内置有__next__方法
    ps:迭代器一定是可迭代对象
    而可迭代对象不一定是迭代器对象
"""



 l = [1,2,3,4]
  生成一个迭代器对象
 iter_l = l.__iter__()

  迭代器取值 调用__next__
 print(iter_l.__next__())
 print(iter_l.__next__())
 print(iter_l.__next__())
 print(iter_l.__next__())
 print(iter_l.__next__())   如果取完了 直接报错


 d = {'name':'jason','password':'123','hobby':'泡m'}
  将可迭代对象d转换成迭代器对象
 iter_d = d.__iter__()
  迭代器对象的取值 必须用__next__
 print(iter_d.__next__())
 print(iter_d.__next__())
 print(iter_d.__next__())
 print(iter_d.__next__())   取完了 报错StopIteration

f1 = open('xxx.txt','r',encoding='utf-8')
 调用f1内置的__iter__方法
 iter_f = f1.__iter__()
 print(iter_f is f1)
"""
迭代器对象无论执行多少次__iter__方法得到的还是迭代器对象本身(******)
"""
 print(f1 is f1.__iter__().__iter__().__iter__().__iter__())
"""
问:__iter__方法就是用来帮我们生成迭代器对象
而文件对象本身就是迭代器对象,为什么还内置有__iter__方法???
"""





d = {'name':'jason','password':'123','hobby':'泡m'}
 iter_d = d.__iter__()


 print(d.__iter__().__next__())
 print(d.__iter__().__next__())
 print(d.__iter__().__next__())

 print(iter_d.__next__())
 print(iter_d.__next__())
 print(iter_d.__next__())
 print(iter_d.__next__())

 异常处理
 while True:
     try:
         print(iter_d.__next__())
     except StopIteration:
          print('老母猪生不动了')
         break


 f = open('xxx.txt','r',encoding='utf-8')
 iter_f = f.__iter__()
 print(iter_f.__next__())
 print(iter_f.__next__())
 print(iter_f.__next__())



"""
迭代器取值的特点
    1.只能往后依次取 不能后退

"""
d = {'name':'jason','password':'123','hobby':'泡m'}
 for i in d:
     print(i)
 for循环后面的in关键 跟的是一个可迭代对象
"""
for循环内部的本质
    1.将in后面的对象调用__iter__转换成迭代器对象
    2.调用__next__迭代取值
    3.内部有异常捕获StopIteration,当__next__报这个错 自动结束循环
"""
 for i in 1:
     pass
 iter(1)


"""
可迭代对象:内置有__iter__方法的
迭代器对象:既内置有__iter__也内置有__next__方法

迭代取值:
    优点
        1.不依赖于索引取值
        2.内存中永远只占一份空间,不会导致内存溢出
    
    缺点
        1.不能够获取指定的元素
        2.取完之后会报StopIteration错

"""
 l = [1,2,3,4]
 res = map(lambda x:x+1,l)
 print(map(lambda x:x+1,l))
 print(res.__next__())
 print(res.__next__())
 print(res.__next__())


l1 = [1,2,3,4,5]
l2 = ['a','b','c']
print(zip(l1,l2))




"""
生成器:用户自定义的迭代器,本质就是迭代器

"""
 def func():
     print('first')
     yield  666   函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行
     print('second')
     yield  777
     print('third')
     yield  888
     print('forth')
     yield
     yield
  yield后面跟的值就是调用迭代器__next__方法你能得到的值
  yield既可以返回一个值也可以返回多个值 并且多个值也是按照元组的形式返回
 g = func()   生成器初始化:将函数变成迭代器
 print(g)
 print(g.__next__())
 print(g.__next__())



 print(range(1,10))

 for i in range(1,10,2):
     print(i)

def my_range(start,end,step=1):
    while start < end:
        yield start
        start += step


for j in my_range(1,100,2):
    print(j)





 yield支持外界为其传参
def dog(name):
    print('%s 准备开吃'%name)
    while True:
        food = yield
        print('%s 吃了 %s'%(name,food))
 def index():
     pass

 当函数内有yield关键字的时候,调用该函数不会执行函数体代码
 而是将函数变成生成器
 g = dog('egon')
 g.__next__()   必须先将代码运行至yield 才能够为其传值
 g.send('狗不理包子')   给yield左边的变量传参  触发了__next__方法
 g.send('饺子')

"""
yield
    1.帮你提供了一种自定义生成器方式
    2.会帮你将函数的运行状态暂停住
    3.可以返回值

与return之间异同点
    相同点:都可以返回值,并且都可以返回多个
    不同点:
        yield可以返回多次值,而return只能返回一次函数立即结束
        yield还可以接受外部传入的值
"""





 列表生成式
 res = [i for i in range(1,10) if i != 4]
 print(res)

 res = (i for i in range(1,100000000) if i != 4)   生成器表达式
 print(res)
 """
 生成器不会主动执行任何一行代码
 必须通过__next__触发代码的运行
 """
 print(res.__next__())
 print(res.__next__())
 print(res.__next__())
 print(res.__next__())


 一次全部读取整个文件占内存
 f = open('xxx.txt','r',encoding='utf-8')
 data = f.read()
 print(len(data))
 f.close()


 with open('xxx.txt','r',encoding='utf-8') as f:
      n = 0
      for line in f:
          n += len(line)
      print(n)
     g = (len(line) for line in f)
      print(g.__next__())
      print(g.__next__())
      print(g.__next__())
      print(g.__next__())
     print(sum(g))



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 test())

     第二次for循环g=(add(n,i) for i in (add(n,i) for i in test()))
print(n)
res=list(g)

"""
for i in (add(10,i) for i in test()):  会执行所有的生成器内部的代码
    add(n,i)
"""




A. res=[10,11,12,13]
B. res=[11,12,13,14]
C. res=[20,21,22,23]  答案
D. res=[21,22,23,24]
 print(abs(-11.11))   求绝对值
 l = [0,1,0]
 print(all(l))   只要有一个为False就返回False
 print(any(l))   只要有一个位True就返回True
def index():

    username = '我是局部名称空间里面的username'
     print(locals())   当前语句在哪个位置 就会返回哪个位置所存储的所有的名字
    print(globals())   无论在哪 查看的都是全局名称空间
 index()
 print(bin(10))
 print(oct(10))
 print(hex(10))
 print(int('0b1010',2))

 print(bool(1))
 print(bool(0))


 s = 'hello'
 print(s.encode('utf-8'))
 print(bytes(s,encoding='utf-8'))

 可调用的(可以加括号执行相应功能的)
 l = [1,2,3]
 def index():
     pass
 print(callable(l))
 print(callable(index))





 print(chr(97))   将数字转换成ascii码表对应的字符
 print(ord('a'))   将字符按照ascii表转成对应的数
 dir获取当前对象名称空间里面的名字
 l = [1,2,3]
 print(dir(l))

 import test
 print(dir(test))
 print(test.name)

 divmod  分页器

 print(divmod(101,10))
 total_num,more = divmod(900,11)
 if more:
     total_num += 1
 print('总页数:',total_num)

 enumerate 枚举
 l = ['a','b']
 for i,j in enumerate(l,1):
     print(i,j)

 eval  exec
s = """
print('hello baby~')
x = 1
y = 2
print(x + y)

"""
 eval(s)
 exec(s)

 eval不支持逻辑代码,只支持一些简单的python代码
s1 = """
print(1 + 2)
for i in range(10):
    print(i)
"""
 eval(s1)
 exec(s1)


 name = 'jason'
 s2 = """
 name
 """
 print(eval(s2))

 format 三种玩法
 {}占位
 {index} 索引
 {name} 指名道姓

 print(globals())
def login():
    """
    一起嗨皮
    :return:
    """
 print(help(login))

 isinstance 后面统一改方法判断对象是否属于某个数据类型
 n = 1
 print(type(n))
 print(isinstance(n,list))   判断对象是否属于某个数据类型

 print(pow(2,3))


 print(round(3.4))


"""
面向过程编程:就类似于设计一条流水线
    好处:
        将复杂的问题流程化 从而简单化
    坏处:
        可扩展性较差   一旦需要修改 整体都会受到影响
"""

  迭代器示例

import sys
 
def fibonacci(n): # 生成器函数 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
 
while True:
    try:
        print (next(f), end=" ")
    except StopIteration:
        sys.exit()

  

class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
    if self.a <= 20:
      x = self.a
      self.a += 1
      return x
    else:
      raise StopIteration
 
myclass = MyNumbers()
myiter = iter(myclass)
 
for x in myiter:
  print(x)

  总结 :

迭代器 和生成器 其实都是一种类型,既都可以理解为 一个 按钮,你按一下 出一次结果,以此来达到节省内存的效果,注意的是 ,当 yeild被赋值时,需要考虑形参的传递,函数如果含有yeild 那么 函数名()并不会直接运行函数,而是生成器的初始化,然后我们可以 不断的用函数名__next__()的方法 获取yeild右边的返回值,随后yeild的左边的值会被传入下一次的函数运行中. 

原文地址:https://www.cnblogs.com/Sunbreaker/p/11189600.html