zoukankan      html  css  js  c++  java
  • day13: 迭代器和生成器

    1,思考所有可以被for循环的:list,tuple,set,dict,range,enumerate,f,str,差不多了,为何这些数据类型可以被for循环呢?

    2,一个标准的装饰器函数

    from functools import wraps
    def wrapper(f):
        @wraps(f)
        def inner(*args,**kwargs):
            # 被装饰函数调用之前添加的功能
            ret = f(*args,**kwargs)
            # 被装饰函数调用之后添加的功能
            return ret
        return inner
    
    @wrapper
    def func():
         print("i am func")
         
    func()

    3,查看一个变量或者是数据类型的所有方法dir

    print(dir([])) # 告诉我字典拥有的所有方法
    [].__add__()  # 这些方法都可以通过点来调用

    4,双下方法,一般双下方法我们不会自己去调用,通常是执行其他操作的时候,自动调用的,也叫做魔法方法

    # 运行加操作的时候,会自动调用双下add方法
    print([1]+[2])
    print([1].__add__([2]))

    5,结论,只要是可以被for循环的就有__iter__方法

    # 我想查看一下这些数据有哪些共同的方法
    # ret = set(dir([]))&set(dir({}))&set(dir(''))&set(dir(range(10))) # 求交集
    # print(ret)
    
    # 发现了一个和可迭代的Iterable很像的双下函数__iter__
    
    # 那我们来查看一下intl类型是否有这个函数
    print("__iter__" in dir(int))  #结果为false
    print("__iter__" in dir(bool))
    # 看看其他的类型有没有这个函数
    print("__iter__" in dir(str))
    print("__iter__" in dir(dict))
    print("__iter__" in dir(set))
    print("__iter__" in dir(list))
    print("__iter__" in dir(tuple))
    print("__iter__" in dir(enumerate))
    print("__iter__" in dir(range))
    
    运行结果:
    False
    False
    True
    True
    True
    True
    True
    True
    True

    6,那我们看一下者个__iter__是个什么鬼?结论:他们都是iterator,所有这些数据,执行了__iter__之后返回的都是一个iterator,就是迭代器

    print([])
    print([].__iter__())
    print(list.__iter__([]))
    print({}.__iter__())
    print({1:2}.__iter__())
    print(range(3).__iter__())
    print(''.__iter__())
    print(enumerate([]).__iter__())
    print((1,).__iter__())
    
    运行结果:
    []
    <list_iterator object at 0x10b127630>
    <list_iterator object at 0x10b127630>
    <dict_keyiterator object at 0x10b0d8e08>
    <dict_keyiterator object at 0x10b0d8e08>
    <range_iterator object at 0x10b16c4e0>
    <str_iterator object at 0x10b0f77b8>
    <enumerate object at 0x10b200990>
    <tuple_iterator object at 0x10b127630>

    7,看看迭代器独有的函数有哪些? 他们有一个共同的函数就是__next__

    # 那我们看看迭代器比原来的数据多了哪些函数?求差集
    print(set(dir([].__iter__()))-set(dir([])))
    print(set(dir({}.__iter__()))-set(dir({})))
    print(set(dir(''.__iter__()))-set(dir('')))
    print(set(dir(range(10).__iter__()))-set(dir(range(10))))
    # lengh_hint 元素个数
    运行结果:
    {'__length_hint__', '__next__', '__setstate__'}
    {'__length_hint__', '__next__'}
    {'__length_hint__', '__next__', '__setstate__'}
    {'__length_hint__', '__next__', '__setstate__'}

    8,结论,可迭代的,只要含有__iter__方法的就是可迭代的,可迭代协议里面规定的,协议就是约定好了的

    迭代器都是可迭代的,可迭代的不一定是迭代器,只要是可以for循环的就都是可迭代的,就有__iter__方法,只有可迭代的才可以用for

    写成可迭代对象,就是为了用for,不确定是否可以for,判断一下是否是可迭代的。

    9,迭代器,迭代器协议,从容器中一个一个取值,可以取到所有的值,节省内存空间,含有__next__方法和__iter__两个方法

    迭代器并不会在内存中在占用一大块内存,而是随着循环,每次生成一个,每次next在给我一个,要一个给一个,内存中永远只有一个

    而且执行速度不会慢,就算真的一次全部要过来,操作系统,找地存这些数据也是很浪费时间的,所以迭代器还是很有好处的

    my_list =[1,2,3,4]
    my_list_iter =my_list.__iter__()
    
    
    ret = '__iter__' in dir(my_list_iter)
    print(ret)
    ret = '__next__' in dir(my_list_iter)
    print(ret)
    
    ret = '__iter__' in dir(my_list)
    print(ret)
    ret = '__next__' in dir(my_list)
    print(ret)
    
    运行结果:
    True
    True
    True
    False

    10,range ,文件句柄都是可迭代的,range可以切片的

    print(range(1000)) # 这是一个可迭代对象,我给是给他要了1000个数,系统答应了,但是没有给,文件句柄也是,你打开文件时,并没有真正打文件加载到内存而是后面read的时候才去加载
    print(list(range(1000))) # 这样取的话,会占用大量内存,会取出所有的数放到内存里面
    
    
    # range是可以切片的
    
    print(range(1000)[1:100])
    print(list(range(1000)[1:100]))# 同样占用内存

    11,迭代器循环结束会报错,因为取不到下一个值了,这个错误其实可以自己去处理的

    my_list =[1,2,3,4]
    my_list_iter =my_list.__iter__()  # 一个列表执行了__iter__方法返回值就是一个迭代器
    while True:
        print(my_list_iter.__next__())
    
    运行结果:
    1
    Traceback (most recent call last):
    2
      File "/Users/guolixiao/PycharmProjects/lisa's_practise/boys/13.3.py", line 22, in <module>
    3
        print(my_list_iter.__next__())
    4
    StopIteration

    12,如果我想写一个函数,一个一个的给我数据,而不是一次全给我,怎么办?生成器,自己写一个迭代器,其实就是生成器

    我不能一直用next去调用,强制转换,会全部加载到内存,不想生产数据了,我想生产字符串,生成器函数

    13,不想把所有数据一次性加载到内存,还要处处使用它,所以才有了生成器函数,因为普通的迭代器已经不能满足我们要求了,我们要自己来写

    生成器有两种形式:生成器函数,本质就是我们自己写的函数,另外一个是生成器表达式

    def func():
        for i in range(2000000):
            i = 'lisa%s'%i
            return i  # 这样并不能解决我的问题,因为return只可以return一次
    #写到函数里面是因为我要多次使用这个函数

    14,其实__iter__和__next__的字眼,我们在实际编程的时候是不会出现的,但是我们要知道原理,知道我们调用for循环的时候,内部就是这么走的,而且前面这么多也是为了后面生成器做铺垫

    15,既然return不可以,那怎么?yield,生成器本质还是迭代器,就是自己写的迭代器,把普通函数的return改成yeild 就变成了生成器函数

    两者共同点就是都只能在函数内部用,而且不能共用

    def generator():
        print(1)
        yield 'a'
        
    ret = generator()  # 返回值就是一个生成器
    print(ret)
    
    运行结果:
    <generator object generator at 0x103845480>

    16,我们会发现返回的生成器里面是有__iter__和__next__方法的,他就是一个迭代器

    17,我们调用生成器的时候,并不会执行里面的代码,知道我们要用生成器里面的数据的时候才会去运行里面的代码

    def generator(): # 第一步定义一个函数
        print(1)  #第五步执行生成器里面的代码
        yield 'a' # 第六步
    
    ret = generator()  # 第二步调用函数返回给ret
    print(ret)  #第三步打印
    print(ret.__next__()) # 第四步调用生成器里面的数值

    18,多个yield,调用一次next走到一个yield,知道没有了,会抛出错误,内部函数执行会受到外部的控制

    def generator():
        print(1)
        yield 'a'
        print(2)
        yield 'b'
    
    ret = generator()
    print(ret)
    print(ret.__next__()) 
    
    运行结果:
    <generator object generator at 0x105895480>
    1
    a

    19,如何才能不抛出错误,不能用next调用,需要用for 来调用

    def generator():
        yield 'a'
        yield 'b'
        yield 'c'
        yield 'd'
    
    ret = generator()
    print(ret)
    for i in ret:
        print(i)
    
    运行结果:
    <generator object generator at 0x105fb2480>
    a
    b
    c
    d

    20,200万个Lisa字符串

    def generator():  #定义生成器函数
        for i in range(200000):
            yield "lisa%s"%i
    
    gene_lisa = generator()
    
    for i in gene_lisa:
        print(i)
    
    # 运行速度非常快,因为是要一个,给一个的,不占用内存

    21,而且我们可以再取出字符串以后自己在做一些操作,比方说只要前50个,或者每个串前面加三个星号,等等

    22,两个生成器是单独的,不要混了

    def generator():  #定义生成器函数
        for i in range(200000):
            yield "lisa%s"%i
    
    gene_lisa1 = generator()
    gene_lisa2 = generator()
    
    print(gene_lisa1.__next__())
    print(gene_lisa2.__next__())
    
    运行结果:
    lisa0
    lisa0

    23,每次for循环都是一个新的迭代器

    li = [1,2,3,4]
    # 列表不是一个迭代器,只是一个可迭代的,所以每次执行for循环的时候,会转化为一个迭代器,每次
    # 转化出来的都是新的
    
    for i in li:
        print(i )
        if i == 2:
            break
    
    for i in li:
        print(i)

    24,实现一个监听用户输入的功能,代码和老师一模一样,但是不监听,为啥呢?

    def tail(filename):
        f1 = open(filename,encoding="utf-8")
            # 下面我不可以用for循环,read或者readlines ,因为这些运行完了都会自动结束
            # 怎么办,我们用while True,这个会一直执行,不会结束,只不过后面打印的都是空而已
        while True:
            line = f1.readline()
            if line.strip():  #非空再打印,这样后面就不会覆盖掉了
                yield line.strip()  # 我想要处理一下这个数据了,那么我必须把这个数据返回回去,但是return只能返回一个,所以必须要yield
    
    g = tail("file.txt")
    
    for i in g:
        print(i)

    25,验证可迭代协议,和迭代器协议

    from collections import Iterator   # 迭代器,import就是别人写好了的
    from collections import Iterable   # 可迭代的
    
    print(isinstance([],Iterable))
    print(isinstance([],Iterator))
    
    print(isinstance([].__iter__(),Iterable))
    print(isinstance([].__iter__(),Iterator))
    
    运行结果:
    True
    False
    True
    True

    26,只有next啥也不是

    # 自己定义一个数据类型
    class A:
        def __iter__(self):pass
        def __next__(self):pass
    
    # 实例化一个对象
    a = A()
    print(isinstance(a,Iterable))
    print(isinstance(a,Iterator))
    
    运行结果:
    True
    True

    27,

    li = [1,2,3,4]
    
    for i in li:
        print(i)
    
    for i in li.__iter__():
        print(i)
    
    for i in li.__iter__().__iter__(): # 这个其实就没有意义了,前面已经是可迭代的了
        print(li)
    
    运行结果:
    1
    2
    3
    4
    1
    2
    3
    4
    [1, 2, 3, 4]
    [1, 2, 3, 4]
    [1, 2, 3, 4]
    [1, 2, 3, 4]

    28,可迭代的.__iter__方法就可以得到一个迭代器,猜测是一个可迭代的,类似range这种,告诉你有10个数,实际上没给你,就可以用for来拿

    直接告诉你是一个可迭代器,直接告诉你内存地址的

  • 相关阅读:
    some requirement checks failed
    FTP下载文件时拒绝登陆申请怎么办?
    Linux查看与设定别名
    如何编写shell脚本
    Linux shell是什么
    Linux命令大全之查看登陆用户信息
    Linux命令大全之挂载命令
    论第二次作业之输入输出格式怎么合格(才疏学浅说的不对轻点喷我)
    文件词数统计
    软件工程作业--第一周
  • 原文地址:https://www.cnblogs.com/lisa-blog/p/10083718.html
Copyright © 2011-2022 走看看