码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 3.0 Python 迭代器与生成器


    合集 - 灰帽黑客:攻守道(83)
    1.1.13 导出表劫持ShellCode加载09-012.1.12 进程注入ShellCode套接字09-013.1.10 内存ShellCode注入与格式化08-314.1.9 动态解密ShellCode反弹08-315.1.8 运用C编写ShellCode代码08-306.1.7 完善自定位ShellCode08-307.1.6 编写双管道ShellCode08-298.1.5 编写自定位ShellCode弹窗08-299.1.4 编写简易ShellCode弹窗08-2810.1.3 Metasploit 生成SSL加密载荷08-2811.1.1 Metasploit 工具简介08-2812.5.14 汇编语言:仿写Switch选择结构08-2413.5.13 汇编语言:仿写For循环语句08-2414.5.12 汇编语言:仿写While循环语句08-2415.5.11 汇编语言:仿写IF条件语句08-2416.5.10 汇编语言:汇编过程与结构08-2417.5.9 汇编语言:浮点数操作指令08-2318.5.8 汇编语言:汇编高效除法运算08-2319.5.7 汇编语言:汇编高效乘法运算08-2320.5.6 汇编语言:汇编高效数组寻址08-2321.5.5 汇编语言:函数调用约定08-2222.5.4 汇编语言:算数运算指令集08-2223.5.3 汇编语言:字符串操作指令08-2224.5.2 汇编语言:标志位测试指令08-2225.5.1 汇编语言:汇编语言概述08-2226.4.9 C++ Boost 命令行解析库08-2227.4.8 C++ Boost 应用JSON解析库08-2228.4.7 C++ Boost 多线程并发库08-2129.4.6 C++ Boost 函数绑定回调库08-2130.4.5 C++ Boost 文件目录操作库08-2131.4.4 C++ Boost 数据集序列化库08-2132.4.3 C++ Boost 日期时间操作库08-1833.4.2 C++ Boost 内存池管理库08-1834.4.1 C++ Boost 字符串处理库08-1835.11.1 C++ STL 应用字典与列表08-1736.10.1 C++ STL 模板适配与迭代器08-1737.9.1 C++ STL 排序、算数与集合08-1738.8.1 C++ STL 变易拷贝算法08-1639.7.1 C++ STL 非变易查找算法08-1640.6.1 C++ STL 序列映射容器08-1641.5.1 C++ STL 集合数据容器08-1642.4.1 C++ STL 动态链表容器08-1643.3.1 C++ STL 双向队列容器08-1644.2.1 C++ STL 数组向量容器08-1645.1.1 C++ STL 字符串构造函数08-1646.7.5 C/C++ 实现链表队列08-1547.7.4 C/C++ 实现链表栈08-1548.7.3 C/C++ 实现顺序栈08-1549.7.2 C/C++ 实现动态链表08-1550.7.1 C/C++ 实现动态数组08-1551.9.0 Python 内置模块应用08-1452.8.0 Python 使用进程与线程08-1453.7.0 Python 面向对象编程08-1454.6.0 Python 使用函数装饰器08-1455.5.0 Python 定义并使用函数08-1356.4.0 Python 变量与作用域08-13
    57.3.0 Python 迭代器与生成器08-12
    58.2.0 Python 数据结构与类型08-1159.21.1 使用PEfile分析PE文件08-1060.1.0 Python 标准输入与输出08-0961.1.8 运用C编写ShellCode代码07-1362.5.2 基于ROP漏洞挖掘与利用07-1263.5.1 缓冲区溢出与攻防博弈07-1264.2.0 熟悉CheatEngine修改器07-1165.4.9 x64dbg 内存处理与差异对比07-1166.4.10 x64dbg 反汇编功能的封装07-1167.4.8 x64dbg 学会扫描应用堆栈07-1068.4.7 x64dbg 应用层的钩子扫描07-1069.4.6 x64dbg 内存扫描与查壳实现07-0970.4.5 x64dbg 探索钩子劫持技术07-0971.4.4 x64dbg 绕过反调试保护机制07-0872.4.3 x64dbg 搜索内存可利用指令07-0773.4.2 x64dbg 针对PE文件的扫描07-0774.4.1 探索LyScript漏洞挖掘插件07-0675.1.5 为x64dbg编写插件07-0676.1.1 熟悉x64dbg调试器07-0677.1.7 完善自定位ShellCode后门07-0578.1.6 编写双管道ShellCode后门07-0479.1.5 编写自定位ShellCode弹窗07-0380.1.4 编写简易ShellCode弹窗07-0281.1.3 Metasploit 生成SSL加密载荷07-0182.1.1 Metasploit 工具简介06-3083.2.1 PE结构:文件映射进内存09-04
    收起

    阅读目录

    • 3.1 使用迭代器
    • 3.2 使用生成器
    • 3.3 队列的使用

    当我们需要处理一个大量的数据集合时,一次性将其全部读入内存并处理可能会导致内存溢出。此时,我们可以采用迭代器Iterator和生成器Generator的方法,逐个地处理数据,从而避免内存溢出的问题。

    迭代器是一个可以逐个访问元素的对象,它实现了python的迭代协议,即实现了__iter__()和__next__()方法。通过调用__next__()方法,我们可以逐个访问迭代器中的元素,直到所有元素都被访问完毕,此时再次调用__next__()方法会引发StopIteration异常。

    生成器是一种特殊的迭代器,它的实现方式更为简洁,即通过yield语句来实现。生成器函数使用yield语句返回值,当生成器函数被调用时,它会返回一个生成器对象,通过调用__next__()方法来逐个访问生成器中的元素,直到所有元素都被访问完毕,此时再次调用__next__()方法会引发StopIteration异常。

    使用迭代器和生成器可以有效地避免内存溢出问题,并且代码实现也更为简洁、高效。在python中,很多内置函数和语言特性都支持迭代器和生成器的使用,例如for循环、列表推导式、生成器表达式等。

    回到顶部

    3.1 使用迭代器

    迭代器可以通过内置函数iter()进行创建,同时可以使用next()函数获取下一个元素,如果迭代器没有更多的元素,则抛出StopIteration异常在for循环中,迭代器可以自动实现例如for x in my_iterable:语句就可以遍历my_iterable对象的所有元素。此外python中还有一种特殊的迭代器,称为生成器(generator),生成器是一种用简单的方法实现迭代器的方式,使用了yield语句,生成器在执行过程中可以暂停并继续执行,而函数则是一旦开始执行就会一直执行到返回。

    创建基本迭代器: 首先声明列表,然后使用__iter__将其转为迭代器,并通过__next__遍历迭代对象.

    >>> list = [1,2,3,4,5,6,7,8,9,10]
    >>>
    >>> item = list.__iter__()
    >>> type(item)
    <class 'list_iterator'>
    >>>
    >>> item.__next__()
    1
    >>> next(item)
    2
    

    迭代器遍历日志文件: 使用迭代器可以实现对文本文件或日志的遍历,该方式可以遍历大型文件而不会出现卡死现象.

    # 手动访问迭代器中的元素,可以使用next()函数
    >>> with open("passwd.log") as fp:
    ...     try:
    ...             while True:
    ...                     print(next(fp))
    ...     except StopIteration:
    ...             print("none")
    
    # 通过指定返回结束值来判断迭代结束
    >>> with open("passwd.log") as fp:
    ...     while True:
    ...             line = next(fp,None)
    ...             if line is None:
    ...                     break
    ...             print(line)
    

    循环遍历迭代元素: 由于迭代器遍历结束会报错,所以要使用try语句抛出一个StopIteration结束异常.

    >>> listvar = ["吕洞宾", "张果老", "蓝采和", "特乖离", "和香菇", "汉钟离", "王文"]
    >>> item = listvar.__iter__()
    >>>
    >>> while True:
    ...     try:
    ...             temp = next(item)
    ...             print(temp)
    ...     except StopIteration:
    ...             break
    

    迭代器与数组之间互转: 通过使用enumerate方法,并将列表转为迭代器对象,然后将对象转为制定格式.

    >>> listvar = ["吕洞宾", "张果老", "蓝采和", "特乖离", "和香菇", "汉钟离", "王文"]
    >>>
    >>> iter = enumerate(listvar)  # 转换为迭代器
    >>> dict = tuple(iter)         # 转换为元组
    >>> dict
    ((0, '吕洞宾'), (1, '张果老'), (2, '蓝采和'), (3, '特乖离'), (4, '和香菇'), (5, '汉钟离'), (6, '王文'))
    >>>
    >>> dict = list(iter)
    >>> dict
    [(0, '吕洞宾'), (1, '张果老'), (2, '蓝采和'), (3, '特乖离'), (4, '和香菇'), (5, '汉钟离'), (6, '王文')]
    
    回到顶部

    3.2 使用生成器

    生成器是一种可以动态生成数据的迭代器,不同于列表等容器类型一次性把所有数据生成并存储在内存中,生成器可以在需要时动态生成数据,这样可以节省内存空间和提高程序效率.使用生成器可以通过for循环遍历序列、列表等容器类型,而不需要提前知道其中所有元素.生成器可以使用yield关键字返回值,每次调用yield会暂停生成器并记录当前状态,下一次调用时可以从上一次暂停的地方继续执行,而生成器的状态则保留在生成器对象内部.除了使用next()函数调用生成器外,还可以使用send()函数向生成器中发送数据,并在生成器内部使用yield表达式接收发送的数据.

    当我们调用一个生成器函数时,其实返回的是一个迭代器对象
    只要表达式中使用了yield函数,通常将此类函数称为生成器(generator)
    运行生成器时,每次遇到yield函数,则会自动保存并暂停执行,直到使用next()方法时,才会继续迭代
    跟普通函数不同,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器

    在学习生成器之前,需要一些前置知识,先来研究一下列表解析,列表解析是python迭代机制的一种应用,它常用于实现创建新的列表,因此要放置于[]中,列表解析非常灵活,可以用户快速创建一组相应规则的列表元素,且支持迭代操作.

    列表生成式基本语法: 通过列表生成式,我们可以完成数据的生成与过滤等操作.

    >>> ret = [item for item in range(30) if item >0]
    >>> print(ret)
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
    >>>
    >>> ret = [item for item in range(30) if item >3]
    >>> print(ret)
    [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
    >>>
    >>> ret = [item for item in range(30) if item%2!=0]
    >>> ret
    [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]
    

    列表式求阶乘: 通过列表解析式,来实现列表的迭代求阶乘,并且只打印大于2(if x>=2)的数据.

    >>> var = [1,2,3,4,5]
    >>> retn = [ item ** 2 for item in var ]
    >>> retn
    [1, 4, 9, 16, 25]
    >>>
    >>> retn = [ item ** 2 for item in var if item >= 2 ]
    >>> retn
    [4, 9, 16, 25]
    >>>
    >>> retn = [ (item**2)/2 for item in range(1,10) ]
    >>> retn
    [0.5, 2.0, 4.5, 8.0, 12.5, 18.0, 24.5, 32.0, 40.5]
    

    数据转换: 通过使用列表生成式,实现将一个字符串转换成一个合格的列表.

    >>> String = "a,b,c,d,e,f,g,h"
    >>> List = [item for item in String.split(",")]
    >>> List
    ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
    

    数据合并: 通过列表解析式,实现迭代将两个列表按照规律合并.

    >>> temp1=["x","y","z"]
    >>> temp2=[1,2,3]
    >>> temp3=[ (i,j) for i in temp1 for j in temp2 ]
    >>> temp3
    [('x', 1), ('x', 2), ('x', 3), ('y', 1), ('y', 2), ('y', 3), ('z', 1), ('z', 2), ('z', 3)]
    

    文件过滤: 通过使用列表解析,实现文本的过滤操作.

    >>> import os
    
    >>> file_list=os.listdir("/var/log")
    >>> file_log=[ i for i in file_list if i.endswith(".log") ]
    >>> print(file_log)
    ['boot.log', 'yum.log', 'ecs_network_optimization.log', 'ntp.log']
    
    >>> file_log=[ i for i in os.listdir("/var/log") if i.endswith(".log") ]
    >>> print(file_log)
    ['boot.log', 'yum.log', 'ecs_network_optimization.log', 'ntp.log']
    

    接下来我们就来研究一下生成器吧,生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器.

    我们先来看以下两种情况的对比,第一种方法很简单,只有把一个列表生成式的[]中括号改为()小括号,就创建了一个生成器.

    >>> lis = [x*x for x in range(10)]
    >>> print(lis)
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    
    >>> generator = (x*x for x in range(10))
    >>> print(generator)
    <generator object <genexpr> at 0x0000022E5C788A98>
    

    如上例子,第一个lis通过列表生成式,创建了一个列表,而第二个generator则打印出一个内存地址,如果我们想获取到第二个变量中的数据,则需要迭代操作,如下所示:

    >>> generator = (x*x for x in range(10))
    
    >>> print(next(generator))
    0
    >>> print(next(generator))
    1
    >>> print(next(generator))
    4
    >>> print(next(generator))
    9
    

    以上可以看到,generator保存的是算法,每次调用next(generaotr),就计算出他的下一个元素的值,直到计算出最后一个元素,使用for循环可以简便的遍历出迭代器中的数据,因为generator也是可迭代对象.

    >>> generator = (x*x for x in range(10))
    >>> 
    >>> for i in generator:
        print(i,end="")
    
    0149162536496481
    

    生成器表达式并不真正创建数字列表,而是返回一个生成器对象,此对象在每次计算出一个条目后,把这个条目"产生"(yield)出来,生成器表达式使用了"惰性计算"或称作"延迟求值"的机制序列过长,并且每次只需要获取一个元素时,应当考虑使用生成器表达式而不是列表解析.

    >>> import sys
    >>> 
    >>> yie=( i**2 for i in range(1,10) )
    >>> next(yie)
    1
    >>> next(yie)
    4
    >>> next(yie)
    9
    
    >>> for j in ( i**2 for i in range(1,10)):print(j/2)
    
    回到顶部

    3.3 队列的使用

    队列是一个多线程编程中常用的数据结构,它提供了一种可靠的方式来安全地传递数据和控制线程间的访问. 在多线程环境下,如果没有同步机制,多个线程同时访问共享资源,可能会导致数据混乱或者程序崩溃.而Queue队列就是一种线程安全的数据结构,它提供了多个线程访问和操作的接口,可以保证多个线程之间的数据安全性和顺序性. 通过Queue队列,一个线程可以将数据放入队列,而另一个线程则可以从队列中取出数据进行处理,实现了线程之间的通信和协调.

    先进先出队列: 先来介绍简单的队列例子,以及队列的常用方法的使用,此队列是先进先出模式.

    import queue
    
    q = queue.Queue(5)                    #默认maxsize=0无限接收,最大支持的个数
    print(q.empty())                      #查看队列是否为空,如果为空则返回True
    
    q.put(1)                              #PUT方法是,向队列中添加数据
    q.put(2)                              #第二个PUT,第二次向队列中添加数据
    q.put(3,block=False,timeout=2)        #是否阻塞:默认是阻塞block=True,timeout=超时时间
    
    print(q.full())                       #查看队列是否已经放满
    print(q.qsize())                      #队列中有多少个元素
    print(q.maxsize)                      #队列最大支持的个数
    
    print(q.get(block=False,timeout=2))   #GET取数据
    print(q.get())                        
    q.task_done()       #join配合task_done,队列中有任务就会阻塞进程,当队列中的任务执行完毕之后,不在阻塞
    print(q.get())
    q.task_done()
    q.join()            #队列中还有元素的话,程序就不会结束程序,只有元素被取完配合task_done执行,程序才会结束
    
    import queue
    
    def show(q,i):
        if q.empty() or q.qsize() >= 1:
            q.put(i)   #存队列
        elif q.full():
            print('queue not size')
    
    que = queue.Queue(5)   #允许5个队列的队列对象
    for i in range(5):
        show(que,i)
    print('queue is number:',que.qsize())  #队列元素个数
    for j in range(5):
        print(que.get())  #取队列
    print('......end')
    

    后进先出队列: 这个队列则是,后进先出,也就是最后放入的数据最先弹出,类似于堆栈.

    >>> import queue
    >>>
    >>> q = queue.LifoQueue()
    >>>
    >>> q.put("wang")
    >>> q.put("rui")
    >>> q.put("ni")
    >>> q.put("hao")
    >>>
    >>> print(q.get())
    hao
    >>> print(q.get())
    ni
    >>> print(q.get())
    rui
    >>> print(q.get())
    wang
    >>> print(q.get())
    

    优先级队列: 此类队列,可以指定优先级顺序,默认从高到低排列,以此根据优先级弹出数据.

    >>> import queue
    >>>
    >>> q = queue.PriorityQueue()
    >>>
    >>> q.put((1,"python1"))
    >>> q.put((-1,"python2"))
    >>> q.put((10,"python3"))
    >>> q.put((4,"python4"))
    >>> q.put((98,"python5"))
    >>>
    >>> print(q.get())
    (-1, 'python2')
    >>> print(q.get())
    (1, 'python1')
    >>> print(q.get())
    (4, 'python4')
    >>> print(q.get())
    (10, 'python3')
    >>> print(q.get())
    (98, 'python5')
    

    双向的队列: 双向队列,也就是说可以分别从两边弹出数据,没有任何限制.

    >>> import queue
    >>>
    >>> q = queue.deque()
    >>>
    >>> q.append(1)
    >>> q.append(2)
    >>> q.append(3)
    >>> q.append(4)
    >>> q.append(5)
    >>>
    >>> q.appendleft(6)
    >>>
    >>> print(q.pop())
    5
    >>> print(q.pop())
    4
    >>> print(q.popleft())
    6
    >>> print(q.popleft())
    1
    >>> print(q.popleft())
    2
    

    生产者消费者模型: 生产者消费者模型,是各种开发场景中最常用的开发模式,以下是模拟的模型.

    import queue,time
    import threading
    q = queue.Queue()
    
    def productor(arg):
        while True:
            q.put(str(arg))
            print("%s 号窗口有票...."%str(arg))
            time.sleep(1)
    
    def consumer(arg):
        while True:
            print("第 %s 人取 %s 号窗口票"%(str(arg),q.get()))
            time.sleep(1)
    
    for i in range(10):                     #负责生产票数
        t = threading.Thread(target=productor,args=(i,))
        t.start()
    
    for j in range(5):                      #负责取票,两个用户取票
        t = threading.Thread(target=consumer,args=(j,))
        t1 = threading.Thread(target=consumer,args=(j,))
        t.start()
        t1.start()
    

    本文作者: 王瑞
    本文链接: https://www.lyshark.com/post/1c1ebaa1.html
    版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

  • 相关阅读:
    【数仓】数据质量监控
    Git 分支设计规范
    【ROS进阶篇】第五讲 ROS中的TF坐标变换
    你需要知道的 12 个常用的 JavaScript 函数
    一个java文件的JVM之旅 | 京东物流技术团队
    C# 更加优质的操作MongoDB数据库
    微服务实战系列之Sentinel
    Ubuntu系统下使用apt-get安装Mysql8
    Linux底层基础知识
    Vue3 源码阅读(9):渲染器 —— diff 算法
  • 原文地址:https://www.cnblogs.com/LyShark/p/17624639.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号