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

    迭代器

    可迭代对象:具有__iter__方法的都可叫做可迭代对象

    基本数据类型中,是可迭代对象的有str、list、tuple、dict、set、文件对象f(执行内置的__iter__之后还是本身,没有任何变化):文件对象本身就是迭代器对象。

    n = 1
    f = 1.1
    s = 'hello'
    l = [1,2,45]
    t= (1,3,56)
    d = {'name':'jaosn'}
    s1 = {1,3,4,5}
    f1 = open('xxx.txt','w',encoding='utf-8')
    
    res = s.__iter__()
    print(s.__len__())  # 5
    res1 = l.__iter__()
    res2 = f1.__iter__()
    print(res1,res1,res2)
    print(f1)  # 文件本身就是迭代器对象
    
    <list_iterator object at 0x03B87D30> <list_iterator object at 0x03B87D30> <_io.TextIOWrapper name='xxx.txt' mode='w' encoding='utf-8'>
    
    <_io.TextIOWrapper name='xxx.txt' mode='w' encoding='utf-8'>
    
    
    f1 = open('xxx.txt','r',encoding='utf-8')
    iter_f = f1.__iter__()
    print(iter_f is f1)  # True
    

    什么是迭代器:

    ​ 迭代:更新换代(重复的过程),每次的迭代都必须基于上一次的结果

    ​ 迭代器:迭代取值的工具

    为什么要用:

    ​ 迭代器给你提供了一种不依赖于索引取值的方式。

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

    迭代器对象:

    ​ 1、内置有__iter__方法和__next__方法的可迭代对象

    ​ ps:迭代器对象一定是可迭代对象,可迭代对象不一定是迭代器对象。

    迭代器对象取值必须使用__next__方法。

    l = [1,2,3,4]
    iter_l = l.__iter__()
    
    print(iter_l.__next__())
    print(iter_l.__next__())
    print(iter_l.__next__())
    print(iter_l.__next__())
    print(iter_l.__next__())  # 如果取完了,会直接报错
    

    迭代器对象无论执行多少次__iter__方法得到的还是迭代器对象本身。

    print(f1 is f1.__iter__().__iter__().__iter__().__iter__())  # True
    

    问题:

    __iter__方法就是用来帮我们生成迭代器对象。而文件对象本本身就是迭代器对象,为什么还内置有__iter__方法???

    因为利用for循环取值的时候,会自动使用__iter__方法将可迭代对象转化为迭代器对象,进而取值。而如果将文件内置的__iter__方法取消了,就不能利用for循环取出列表的每一行内容。

    迭代器取值的特点:

    ​ 1、只能往后依次取,不能后退。取完之后,会直接报错。

    # 异常处理
    d = {'name':'jsson','password':'123','hobby':'泡面'}
    iter_d = d.__iter__()
    while True:
    	try:
        	print(iter_d.__next__())
        except StopIteration:
    #		print('取完了')
    		break    
    

    for 循环取值的本质

    for循环里面的in关键字后面跟的是一个可迭代对象。

    for循环内部的本质:

    ​ 1.将in后面的对象调用__iter__方法转化成迭代器对象

    ​ 2.调用__iter__迭代取值

    ​ 3.内部有异常捕获StopIteration,当__next__报这个错,循环自动结束

    可迭代对象:内置有__iter__方法的对象

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

    迭代取值的优点:

    ​ 1.不依赖于索引取值,

    ​ 2.内存中永远只占一份空间,不会导致内存溢出。

    缺点:

    ​ 1.不能够获取指定的元素

    ​ 2.取完之后会报StopIteration错

    生成器

    生成器:用户自定义的迭代器,本质就是迭代器,里面含有yield关键字。

    函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行。

    def func():
        print('first')
        yield 666
        print('second')
        yield 777
        print('third')
        yield 888
        print('forth')
        yield 
        yield
    g = func()  # 生成器初始化:将函数变成迭代器    
    print(g)  # <generator object func at 0x00857D30>
    print(g.__next__())  # 666
    print(g.__next__())  # 777
    print(g.__next__())  # 888
    print(g.__next__())  # None
    print(g.__next__())  # None
    
    

    yield后面跟的值就是调用迭代器__next__方法你能得到的值

    yield既可以返回一个值也可以返回多个值,并且多个值也是按照元组的形式返回

    for i in range(1,10):
        print(i)
    
    # 使用生成器生成range(1,10)
    def my_range(start,end,step=1):
        while start < end:
            yield start
            start += step
    
    for i in my_range(1,10,2):
        print(i)
    

    yield关键字

    yield的英文单词意思是生产,在函数中但凡出现yield关键字,再调用函数,就不会继续执行函数体代码,而是会返回一个值。yield支持外界为其传参

    def dog(name):
        print(f'{name}准备开吃了')
        while True:
            food = yield
            print(f'{name}吃了{food}')
    
    g = dog('wangcai')
    g.__next__()  # wangcai准备开吃了 必须先将代码运行至yield,才能为其传值。
    g.send('狗不理包子')  # wangcai吃了狗不理包子  # 给yield左边的变量传值,自动触发__next方法
    g.send('饺子')  # wangcai吃了饺子
    
    

    当函数内有yield关键字的时候,调用该函数不会执行函数体代码,而是将函数变成了生成器。

    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,10) if i != 4)
    print(res)
    
    print(res.__next__())
    print(res.__next__())
    print(res.__next__())
    print(res.__next__())
    print(res.__next__())
    
    

    生成器不会主动执行任何一行代码。必须通过__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 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)
    """
    
    res=[20,21,22,23]
    
    

    面试题一

    def demo():
        for i in range(4):
            yield i
    g = demo()
    g1 = (i for i in g)
    g2 = (i for i in g1)
    
    print(list(g1))  # [0,1,2,3]
    print(list(g2))  # []
    
    # g是第一个生成器,g1取得值来自g,g2的值来自于g1,归根到底还是g,list是可迭代对象,将g1赋予list方法,其实是从g中取值,而且取完了,g2自然就是空值了。
    
    def demo():
        for i in range(4):
            yield i
    g = demo()
    g1 = (i for i in g)
    g2 = (i for i in g1)
    print(next(g))  # 0
    print(next(g1))  # 1
    print(next(g2))  # 2
    
    

    面试题二

    def add(n,i):
        return n+i
    def text():
        for i in range(4):
            yield i
    g = text()
    for n in [1,10]:
        g = (add(n,i) for i in g)
        
    print(list(g))
    # 其结果为[20,21,22,23]
    # 如果将for循环下面的表达式换成g = [add(n,i) for i in g] 结果为[11,12,13,14]
    # 如果将for循环的表达式换成for n in [1,2,10],或者for n in [1,11,10],其结果为[30,31,32,33]
    # 如果是for n in [1,3,6,7,10,11,100,10],其结果为[80,81,82,83]
    
    

    面试二总结:生成器的特点是惰性机制,也就是说你找它要,它才会给你值

    ​ 在上一题的for循环中,不管你循环了多少次,生成器的取值都是[]中的最后一个值

    ​ g在取值之前,是一个内存地址,for循环一直改变着g的内存地址,最后是n=10的内存地址

    ​ for循环一次,g中的值就加一次,每次加的都是n=10的内存地址的值

    ​ for循环第一次:10+0 10+1 10+2 10+3

    ​ for循环第二次:10+10+0 10+10+1 10+10+2 10+10+3

    ​ 如果有第三次:10*3+0 10*3+1 10*3+2 10*3+3

    ​ .........

    常用内置方法

    abs()绝对值

    print(abs(-13.5))  # 13.5
    
    

    all(),any()

    l = [0,1,2]
    print(all(l))  # 只要有一个为False就返回False
    print(any(l))  # 只要有一个为True就返回True
    
    

    bin()/oct()/hex() 二进制/八进制/16进制

    print(bin(17))
    print(oct(17))
    print(hex(17))
    
    0b10001
    0o21
    0x11
    
    

    bool()布尔值

    print(bool(1))  # True
    print(bool(0))  # False
    
    

    bytes()字节二进制

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

    callable()可调用的(可以加括号执行相应功能的)

    l = [1,2,3]
    def index():
        pss
    print(callable(l))  # False
    print(callable(index))  # True
    
    

    chr(),ord()

    print(chr(97))  # 将数字转换成asill码表对应的字符  'a'
    print(ord('a'))  # 将字符按照ascii码表转成对应的数字 97
    
    

    dir()获取当前对象名称空间里面的名字

    l = [1,2,4]
    print(dir(l))
    
    ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
    
    
    

    divmod()分页器

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

    enumerate() 枚举

    l = ['a','b']
    for i,j in enumerate(l,1):  # 带有所以索引的迭代
        print(i,j)
        
    1 a
    2 b    
    
    

    eval(),exec()

    s = """
    print('hello baby~')
    x = 1
    y = 2
    print(x+y)
    """
    print(eval(s))  # eval不支持逻辑代码,支持支一些简单的python代码
    
    
    s = """
    print('hello baby~')
    x = 1
    y = 2
    print(x+y)
    """
    exec(s)  # 支持逻辑代码
    
    hello baby~
    3
    
    

    isinstance()

    # isinstance 判断对象是否属于某个数据类型
    n = 1
    print(type(n))
    print(isinstance(n,list))  # 判断对象是否属于某个数据类型
    
    
    

    pow(),计算第一个数的几次方

    print(pow(2,3))  # 8
    
    

    round()四舍五入

    print(round(3,4))  # 3
    
    

    global()/locals()

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

    面向过程编程

    面向过程编程:就类似于设计一条流水线

    优点:复杂的问题流程化进而简单化

    缺点:扩展性差,一旦需要修改,整体都会受到应影响。

    面试题3

    请写出一下代码的执行结果并解释。

    def multipliers():
        return [lambda x:i*x for i in range(4)]
    print([m(2) for in multipliers()])
    
    
    # 解析:
    # [lambda x:i*x for i in range(4)]
    
    [func(x):return 0*x,func(x):return 1*x,func(x):return 2*x,func(x):return 3*x]
    
    # 闭包函数的延迟绑定,在内层函数执行时才会绑定变量
    
    def multipliers2():
    	list1 = []
    	for i in range(4):  # 0,1,2,3
            def func(x):
                return x * i
            list1.append(func)
        return list1
    print([m(2) for m in multipliers2()])
    m = lambda x:i*x
    m = lambda x:i*x
    m = lambda x:i*x
    # 每次都执行一次m(2),也就是每次都调用了一下匿名函数,注意:此时此刻 匿名函数才真正被调用了,它会引用外层空间的变量i,那么此时i的值是多少呢?
    # 因为for i in range(3)这个循环已经执行完毕,i的值等于3,所以每次当执行m(2)时,i的值都等于3。所以每次调用m(2)的结果都是6.
    [6,6,6,6]
    
    

    改进

    def multipliers():
    	
        return [lambda x, i=i: i*x for i in range(4)]
    # return [lambda x,i=0: i*x, lambda x,i=1: i*x, lambda x,i=2: i*x, lambda x,i=3:i*x i=3]
    # x的引用是2 所以output的结果就是:[0,2,4,6]
    
    
    print([m(2) for m in multipliers()])  
    
    def multipliers2():
        list1 = []
        for i in range(4):
    
            def func(x, i=i):
    
                return x * i
    
            list1.append(func)
    
        return list1
    
    print([m(2) for m in multipliers2()])  # [0, 2, 4, 6]
    
    

    添加了一个i = i后,就给匿名函数,添加了一个默认参数,而python函数中的默认参数,是在python解释器遇到def(i=i)或lambda关键字时,就必须初始化默认参数。

    此时for i in range(4),每循环一次,匿名函数的默认参数i,就需要找一次i的引用。i=0时,第一个匿名函数的默认参数值就是0,i=1时,第二个匿名函数的默认参数值就是1,以此类推。

    需求:读取一个文件并返回每行数据的长度

    with open('text.txt','w',encoding='utf-8') as f:
        for line in range(1000):
            f.write(f'www{line}aaa'*(line + 1) + '
    ')
    
    
    # 列表推导式:处理数据量大的文件会导致内存溢出。
    res = [len(line) for line in open('text.txt','r',encoding='utf-8')]
    # print(res)
    
    
    # 生成器表达式:处理数据量大的文件推荐使用
    res2 = (len(line) for line in open('text.txt','r',encoding='utf-8'))
    print(res2)  # <generator object <genexpr> at 0x000002B3748FD0A0>
    print(next(res2))
    print(next(res2))
    
    
  • 相关阅读:
    -for循环案例(下)
    for循环案例(上)
    for循环
    判断语句案例
    判断语句
    操作符优先级
    windows 下安装图片标注软件 labelling和出错解决
    tf-faster rcnn
    目标检测——Faster R-CNN 详解、Pytorch搭建、训练自己的数据集
    java idea 配置安装
  • 原文地址:https://www.cnblogs.com/zuihoudebieli/p/11192008.html
Copyright © 2011-2022 走看看