zoukankan      html  css  js  c++  java
  • python-迭代器

    字符串、列表、元组、字典、集合都可以被for循环,而int和bool不能被for循环,说明他们是不可迭代的。
    怎么样才能给证明,我们知道的数据类型是可迭代的.使用模块collections中的Iterable和isinstance进行类型的判断。

    from collections import Iterable
    
    dic = {"a": "b", "c": "d"}
    lis = ["a", "b", "c"]
    num = 123
    tu = (1, 2, 3)
    se = {1, 2, 3}
    bo = bool("a")
    str_ = "ABC"
    
    print(isinstance(dic, Iterable))  # True
    print(isinstance(lis, Iterable))  # True
    print(isinstance(num, Iterable))  # False
    print(isinstance(tu, Iterable))   # True
    print(isinstance(se, Iterable))   # True
    print(isinstance(bo, Iterable))    # False
    print(isinstance(str_, Iterable))  # True
    
    

    上述就验证了bool和int不是可迭代的对象,其余的数据类型是可迭代的。

    可迭代协议

      可以被迭代要满足的要求就叫做可迭代协议,可迭代协议:就是内部实现了__iter__方法。

    print(set(dir(str)) & set(dir(dict)) & 
          set(dir(list)) & set(dir(tuple)) & set(dir(set)))  # 求交集
    

    数据类型如果想要是可跌代的,在内部必须含有__iter__方法

    dic = {"a": "b", "c": "d"}
    lis = ["a", "b", "c"]
    tu = (1, 2, 3)
    print(dic.__iter__())  # <dict_keyiterator object at 0x000001F86CEA0598>
    print(lis.__iter__())  # <list_iterator object at 0x000001F86CEBCC50>
    print(tu.__iter__())   # <tuple_iterator object at 0x000001F86CEBCCC0>
    
    

    迭代器协议

      迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。

    相对于迭代器列表和列表中多了什么样的方法。

    dir_iter_list = dir([1, 2].__iter__())  # 列表迭代器的方法
    dir_list = dir([1,2])  # 列表的方法
    # 进行差集运算
    print(set(dir_iter_list)-set(dir_list))  # {'__setstate__', '__length_hint__', '__next__'}  
    
    

    上述的运算结果可以看出,列表迭代器的方法比列表的方法多了{'__setstate__', '__length_hint__', '__next__'}

    dir_iter_list_length = [1, 2].__iter__().__length_hint__()
    print(dir_iter_list_length)  # 2 获取迭代器中元素的长度
    # 根据索引值指定从哪里开始迭代
    print([1, 2, 3, 4, 5, 6].__iter__().__setstate__(3))????  
    
    # 元素一个一个的取
    print(lis.__next__())  # a
    print(lis.__next__())  # b
    print(lis.__next__())  # c
    
    

    在进行__next__进行取值的时候,如果将所有的都去完之后,在取值,则将会报错!!!

    lis = ["a", "b", "c"]  # 列表
    lis_iter = lis.__iter__()  # 列表迭代器
    
    print(lis_iter.__next__())  # a
    print(lis_iter.__next__())  # b
    print(lis_iter.__next__())  # c  元素已经是最后一个
    print(lis_iter.__next__())  #  StopIteration
    
    

    在这种情况下,可以采取异常处理的机制进行处理。

    lis = ["a", "b", "c"]  # 列表
    lis_iter = lis.__iter__()  # 列表迭代器
    while 1:
        try:  # 异常处理
            print(lis_iter.__next__())
        except StopIteration as e:
            print("end!!!")
            break
    
    
    

    range()是迭代器吗?还是一个迭代的对象

    from collections import Iterable
    
    print(isinstance(range, Iterable))  # False
    print("__next__" in dir(range))  # False
    print("__iter__" in dir(range))  # True  
    

    上述的代码说明了,range是一个可迭代的对象,但是不是应该迭代器。

    生成器

    迭代器的:一种的调用方法直接返回,一种是可迭代对象通过执行iter方法得到,迭代器的好处就是可以节省内存。
    在很多是时候,由于硬件资源的缺少,在写相关的程序的时候,往往考虑到内存的问题,怎么样才能在代码上进行实现?
    这个就是叫一个生成器的东西。

    在python中提供的生成器:

    • 常规的函数中,使用yield来代替return语句返回的结果,在yield语句中,yield只能放回一个结果,在每个结果的中间,挂起函数的状态,也便下次函数从原先的位置进行执行。
    • 生成器表达式:类似于列表的推导式,会产生一个对象,而不是产生一个结果列表,在生成器表式中,是也()进行创建的。
    M = [[1,2,3],[4,5,6],[7,8,9]]
    
    data = (i[-1] for i in M )
    print(data)  # <generator object <genexpr> at 0x000001B892E615C8>
    

    生成器函数

      一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束

    def generator():
        print(111)
        yield "aaa"  # yield 生成器的关键字
        print(222)
        yield "222"
    
    
    g = generator()
    print(g)  # 是一个生成器 <generator object generator at 0x00000193C07615C8>
    print(g.__next__())
    print(g.__next__())
    

    上述的代码和下面的带会产生什么样的结果?

    def generator():
        print(111)
        yield "aaa"  # yield 生成器的关键字
        print(222)
        yield "222"
    
    print(generator().__next__())
    print(generator().__next__())
    print(generator().__next__())
    print(generator().__next__())
    结果:
    '''
    111
    aaa
    111
    aaa
    111
    aaa
    111
    aaa
    '''
    # 为什么是111 aaa 为什么没有222 "222"
    产生这个的原因的generator().__netxt__()是一个生成器去实现了__next__方法,下面的generator().__next__()也相当于是另外一个生成器对象,只能打印111和返回"aaa",下面的222 "222"永远不实现。而上述的代码就可以实现,因为调用的同一个生成器,代码就会向下指向,执行到下一个yield
    

    生成器的本质就是的一个迭代器
    生成器的好处就是节约内存。

    __next__()和send()

    send()和__next__()一样可以执行下一个yield

    def func():
        print(1)
        a = yield 2  # 1.挂起 2.返回值 3.接受值
        print(a)   # '123'
        print(3)
        b = yield 4
        print(b)   #'234'
        c = yield 9
    
    g = func()
    
    print(g.__next__())   #1 2  g.send(None)
    print(g.send('123'))  # send = next+传值
    print(g.send('234'))  # send = next+传值
    # # 第一次调用生成器的时候使用send里边的值必须是None
    
    ''''
    1
    2
    123
    3
    4
    234
    9
    '''
    
    def func():
        print("start")
        a = yield 1
        print(a)
        b = yield 2
        print(b)
        yield "abc"
        print("end")
    
    
    
    f = func()
    print(f.send(None)) 
    print(f.send(18))#
    print(f.send(20))
    
    '''
    start
    1
    18
    2
    20
    abc
    '''
    

    send是将括号中的内容传给了上一yield,然后yield接收的值就可以赋值给变量

    send和__next__()区别:

    • send 和 next()都是让生成器向下走一次

    • send可以给上一个yield的位置传递值, 在第一次执行生成器的时候不能直接使用send(),但是可以使用send(None)

    for获取内部的元素

    def func():
        print("start")
        a = yield 1
        print(a)
        b = yield 2
        print(b)
        yield "abc"
        print("end")
    
    f = func()
    for i in f:
        print(i, end=" ")
    '''
    start
    1 None
    2 None
    abc end
    '''
    
    
  • 相关阅读:
    重新整理 .net core 实践篇————配置系统之盟约[五]
    重新整理 .net core 实践篇————依赖注入应用之援军[四]
    重新整理 .net core 实践篇————依赖注入应用之生命法则[三]
    重新整理 .net core 实践篇————依赖注入应用[二]
    重新整理 .net core 实践篇————配置应用[一]
    spring cloud 学习笔记 客户端(本地)均衡负载(三)
    Leetcode之插入区间
    Leetcode之两棵二叉搜索树中的所有元素
    Leetcode之二叉树的层序遍历
    LeetCode之验证二叉搜索树
  • 原文地址:https://www.cnblogs.com/yangchangjie150330/p/10556225.html
Copyright © 2011-2022 走看看