python 生成器的作用

1. 生成器

参考:
https://www.cainiaojc.com/python/python-generator.html

1.1. 什么是生成器?

在 python 中,一边循环一边计算的机制,称为生成器:generator.

1.2. 生成器有什么优点?

1、节约内存。python在使用生成器时对延迟操作提供了支持。所谓延迟,是指在需要的时候才产生结果,而不是立即产生结果。这样在需要的时候才去调用结果,而不是将结果提前存储起来要节约内存。比如用列表的形式存放较大数据将会占用不少内存。这是生成器的主要好处。比如大数据中,使用生成器来调取数据结果而不是列表来处理数据,因为这样可以节约内存。
2、迭代到下一次的调用时,所使用的参数都是第一次所保留下的。

1.3. 在python中创建生成器

在python中,有两种创建生成器的方式:

1.3.1. 生成器表达式

类似与列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表。
使用列表推导,将会一次返回所有结果:

li=[i**2 for i in range(5)]
print(li)

运行结果:

[0, 1, 4, 9, 16]

将列表推导的中括号,替换成圆括号,就是一个生成器表达式:

li=(i**2 for i in range(5))
print(li)
for x in range(5):
    print(next(li))

运行结果:

<generator object <genexpr> at 0x7f124b6c8120>
0
1
4
9
16

注意:创建完成生成器后,可以使用next()来调用生成器的数据结果,每调用一次返回一个值,直到调用结束。调用结束后li中为空的,不在有任何值。要想使用它,只能重新创建新的生成器。(生成器表达式的第四行代码也可以改成:print(x).)

1.3.2. 生成器函数

常规函数定义,但是使用 yield 语句而不是 return 语句返回结果。yield 语句每次返回一个结果,但每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。我们下面来看一个例子。下面为一个可以无穷生产奇数的生成器函数。

def odd():
    n = 1
    while True:
        yield n
        n += 2

odd_num = odd()
count = 0
for o in odd_num:
    if count >= 5:
        break
    print(o)
    count += 1

运行结果

1
3
5
7
9

上面的函数中,yield 是必备的,当一个普通函数中包含 yield 时,系统会默认为是一个 generator。

1.3.3. 生成器的两种方法

next() 和 send()
其中,next() 方法和 next 的作用是一样的。如下所示。

def fib(times):
    i=0 # 判断退出使用
    n=1 # 计算奇数使用
    while i < times:
        yield n
        n+=2
        i+=1

f = fib(5)
for index in range(5):
	# print(next(f))
    print(f.__next__())

运行结果:

1
3
5
7
9

从上面的程序中可以看出,f.next() 和 next(f) 是作用是一样的。
下面再来看看send() 方法的使用。

def fib(times):
    i=0 # 判断退出使用
    n=1 # 计算奇数使用
    while i < times:
    	# 添加变量 temp
        temp = yield n
        print("fib", temp)
        n+=2
        i+=1

f = fib(5)

print(f.__next__())
print(f.send("haha"))
print(f.send("heihei"))

运行结果:

1
fib haha
3
fib heihei
5

从上面代码可以看出:使用 send() 时,必须在 yield 前面加上一个变量,并打印这个变量。在调用 send() 方法时其前面需要至少使用一次next() 或 next() 方法,因为生成器不可以在使用之前导入任何非 None 参数。由此可以知道,send() 是用来向生成器中导入参数并返回该参数的,与该参数一起返回的还有生成器中原先保存的数据。

1.3.4. 再看生成器

前面已经对生成器有了感性的认识,我们以生成器函数为例,再来深入探讨一下 python 的生成器:
1、语法上和函数类似:生成器函数和常规函数几乎是一样的。它们都是使用 def 语句进行定义,差别在于,生成器使用 yield 语句返回一个值,而常规函数使用 return 语句返回一个值。
2、自动实现迭代器协议:对于生成器,python 会自动实现迭代器协议,以便应用到迭代背景中(如for循环,sum函数)。由于生成器自动实现了迭代器协议,所以,我们可以调用它的 next() 方法,并且,在没有值可以返回的时候,生成器自动产生 StopIteration 异常
3、状态挂起:生成器使用 yield 语句返回一个值。yield 语句挂起该生成器函数的状态,保留足够的信息,以便以后从它离开的地方继续执行。
生成器的唯一注意事项就是:生成器只能遍历一次

2. 迭代器

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历位置的对象。迭代器只能往前不能后退。
1、什么是可迭代对象
集合数据类型,如 list 、tuple、dict、set、str 等
生成器和带 yield 的 generator function
2、如何判断对象可迭代?
from collections import Iterable
isinstance([ ],Iterable)

try:
    from collections.abc import Iterable
except ImportError:
    from collections import Iterable
#from collections import Iterable
print(isinstance([], Iterable))
print(isinstance("abcdef", Iterable))
print(isinstance((x for x in range(5)), Iterable))
print(isinstance(123456, Iterable))
print(isinstance(True, Iterable))

运行结果:

True
True
True
False
False

如上,命令行模式下,先导入Iterable模块,然后输入isinstance([ ],Iterable),括号中前面为待判断的对象,结果以布尔类型结束(True或False),列表是可迭代对象,因此返回True。注意:整数是不可迭代对象。

故障排除:
ImportError: cannot import name ‘Iterable’ from ‘collections’ (/home/lianap/anaconda3/lib/python3.10/collections/init.py)
参考:
https://blog.csdn.net/weixin_55201180/article/details/131345280
3、什么是迭代器呢?
**迭代器是可以被 next() 函数调用并不断返回下一个值的对象称为迭代器。**因此生成器是迭代器的子类,但是注意集合类型的可迭代对象不是迭代器。
iter() 函数:将可迭代对象转换成迭代器。

print(isinstance(iter([1, 2, 3, 4]), Iterable))
print(isinstance([1, 2, 3, 4], Iterable))

运行结果:

True
True

以上描述持怀疑态度。

3. 闭包

3.1. 什么是闭包

内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。
**闭包三要素:**嵌套函数,变量的引用,返回内部函数

# 定义一个嵌套函数(要素1)
def test(num):
    def test_in(num_in):
        # 内部函数引用外部函数的变量(非全局变量)(要素2)
        print("sum=%s"%(num + num_in))
        # 返回的结果可以被打印出来
        return num,num_in
    # 返回内部的函数(要素3)
    return test_in

# 这里的rtn就是 test_in
rtn = test(10)
print(rtn)

# 内部函数test_in传参
print(rtn(20))

运行结果:

<function test.<locals>.test_in at 0x7fbf353c9900>
sum=30
(10, 20)

3.2. 闭包的应用

#求一元一次方程的值,输入x,求y
def fun(a,b):     #其中a,b是固定的
    def fun_in(x):
        return a*x+b
    return fun_in
f = fun(3,5)
print(f(1))    #每次输入不同的x值即可求出对应的y值
print(f(3))

运行结果:

8
14

上面的函数中,利用闭包来求一元一次方程的值,更方便,直接输入x的值即可求出对应的y的值。因为这利用了闭包可以记住外部函数的参数的特性。

4. 装饰器

4.1. 什么是装饰器(decorator)

装饰器其实就是一个闭包,把一个函数当作参数然后返回一个替代版函数。
装饰器有2个特性:
1、可以把被装饰的函数替换成其他函数
2、可以在加载模块时候立即执行
举例如下:
A、装饰器对无参数函数进行装饰:

# 装饰器本质就是一个闭包
# 将函数当做参数传进去
def deco(fun):
    def inner():
        print("你想进行的操作1")
        fun()
        print("你想进行的操作2")
    return inner

# @deco 是一个“语法糖”
@deco
def test():
    print("test函数的操作")

# 这个test已经被装饰了,不是原来的test
# 等同于
# rtn = deco(test)
# rtn()
test()

运行结果:

你想进行的操作1
test函数的操作
你想进行的操作2

4.2. 装饰器的功能有

1、引入日志
2、函数执行时间统计
3、执行函数前预备处理
4、执行函数后清理功能
5、权限校验等场景
6、缓存

4.3. 装饰器的分类

装饰器可分为:对有无参数函数进行装饰的装饰器,对有无返回值函数进行装饰的装饰器,组合起来一共有4种。
即:装饰器对无参数无返回值的函数进行装饰,装饰器对无参数有返回值的函数进行装饰,装饰器对有参数无返回值的函数进行装饰,装饰器对有参数有返回值的函数进行装饰。

4.3.1. 装饰器对无参数函数进行装饰

#定义函数:完成包裹数据
def makeBold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

#定义函数:完成包裹数据
def makeItalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

@makeBold
def test1():
    return "hello world - 1"

@makeItalic
def test2():
    return "hello world - 2"

@makeBold
@makeItalic
def test3():
    return "hello world - 3"

print(test1())
print(test2())
print(test3())

运行结果:

<b>hello world - 1</b>
<i>hello world - 2</i>
<b><i>hello world - 3</i></b>

上面这个例子是做网页的时候对字体进行设置,对 test1() 进行加粗,对 test2() 斜体处理,对 test3() 先斜体在加粗。
注意:对一个函数可以同时使用多个装饰器,装饰顺序由内而外。

4.3.2. 装饰器对有参数函数进行装饰

# 定义一个装饰器
def deco(func):
    def inner(a, b):    # 内部函数的参数必须和被装饰的函数保持一致
        print("添加的功能1")
        func(a, b)
        print("添加的功能2")

    return inner

@deco
# 有参数的函数
def sum(a, b):
    print(a+b)

sum(10,20)

运行结果:

添加的功能1
30
添加的功能2

当装饰器装饰有参数的函数时,装饰器内部的函数也必须带有和其相同的参数,因为被装饰的参数会被当成参数传进装饰器的内部函数中,如果两者的参数不一致,会报错。

4.3.3. 装饰器对不定长参数函数进行装饰

from time import ctime,sleep
#定义一个装饰器,装饰不定长参数函数
def deco(func):
    def inner(*args, **kwargs):
        print("%s called at the %s"%(func.__name__,ctime()))
        func(*args,**kwargs)
    return inner

@deco
def test1(a, b, c):
    print(a+b+c)

@deco
def test2(a, b):
    print(a+b)

test1(10,20,30)
sleep(2)
test1(30,40,50)

sleep(1)
test2(10,20)

运行结果:

test1 called at the Tue Nov 28 22:23:36 2023
60
test1 called at the Tue Nov 28 22:23:38 2023
120
test2 called at the Tue Nov 28 22:23:39 2023
30

4.3.4. 装饰器对有返回值的函数进行装饰

from time import ctime,sleep
#定义一个装饰器,装饰不定长参数函数
def deco(func):
    def inner(*args,**kwargs):
        print("%s called at the %s"%(func.__name__,ctime()))
        return func(*args,**kwargs)
    return inner
#上面的装饰器是一个万能的装饰器,因为它可以装饰任意一种函数
#包括有无参数函数和有无返回值函数

@deco
def test():
    return "---ha--ha---"

t = test()
print(t)

运行结果:

test called at the Tue Nov 28 22:26:46 2023
---ha--ha---

该装饰器是一个万能的装饰器,因为其可以用来装饰任何函数,包括有无参数函数和有无返回值函数。
参考:
https://blog.51cto.com/u_39029/6405726

5. 协程

协程不是计算机提供的,是人为创造的上下文切换技术,也可以被称为微线程。简而言之 其实就是在一个线程中实现代码块相互切换执行。
可以采用以下几种基于协程的方式:
greenlet。
yield 关键字
asyncio 装饰器(py3.4之后引入)
async、await关键字(py3.5之后引入)【推荐】

5.1. greenlet 实现协程

# greenlet是第三方模块需先引入
pip3 install greenlet
# -*- coding: utf-8 -*-
from greenlet import greenlet

def func1():
    print(1)        # 第二步:输出1
    gr2.switch()    # 第三步:切换到 func2 函数
    print(2)        # 第六步:输出2
    gr2.switch()    # 第七步:切换到func2 函数(如果不切换的话句柄会继续往下执行,也就不会进入func2 输出4)

def func2():
    print(3)        # 第四步:输出3
    gr1.switch()    # 第五步:切换到func1 函数
    print(4)        # 第八步:输出4,func2函数 执行完毕句柄继续往下执行

def func3():
    print(5)        # 第十步:输出5

gr1 = greenlet(func1)  # 此处只是生成 greenlet 包装的 func1 对象,代码并不会实际运行
gr2 = greenlet(func2)  # 此处生成 greenlet 包装的 func2 对象

gr1.switch()  			# 第一步:此处是正式执行 func1() 对象
func3() 	 			# 第九步:实例化 func3

运行结果:

1
3
2
4
5

5.2. yield 关键字

不推荐,实际应用场景比较少。

def func1():
    yield 1
    yield from func2()  # 这里其实相当于for item in func2(): yield item
    yield 2

def func2():
    yield 3
    yield 4

for item in func1():
    print(item)

运行结果

1
3
4
2

5.3. asyncio 模块

在 python3.4 及之后的版本才可使用,这个框架使用事件循环来编排回调和异步任务。事件循环位于事件循环策略的上下文中。
下图是协程,事件循环和策略之间的相互作用
asyncio
注意:asyncio牛逼在于遇到IO阻塞自动切换!
下面我们使用 @asyncio.coroutine 装饰器(py3.10+会移除) 定义了两个协程函数。(基于生成器的协程)
详情参考:
https://www.cnblogs.com/micheryu/p/15779377.html

5.4. async & await 关键字【推荐🌟】

py3.5 及之后版本
本质上和3.4的 asyncio 一致,但更强大。
3.5之后 yield from 不可以在 async 定义的函数内使用,需使用 await。

import asyncio

async def func1():
	print(1)
	await asyncio.sleep(2)  # 遇到IO自动切换任务
	print(2)

async def func2():
	print(3)
	await asyncio.sleep(2)  # 此处又遇到IO阻塞后,又会自动切换到tasks中其他的任务
	print(4)

tasks = [
	asyncio.ensure_future(func1()),  # 把协程对象包转成一个 future 对象
	asyncio.ensure_future(func2())
]

loop = asyncio.get_event_loop()
# 执行单个协程函数
loop.run_until_complete(func1())  # 由于func1中使用了await关键字所以此处等同于asyncio.wait
""" 输出结果为:
1
等待2s
2
"""
# 执行多个协程函数
loop.run_until_complete(asyncio.wait(tasks))
""" 输出结果为:
1
3
等待2s
2
4
"""

注:python3.7 之后可以不需要自己获取 loop 对象,可以直接调用 asyncio.run 方法内部已经帮我们获取了loop对象和调用loop.run_until_complete

5.5 实例

  1. async 定义的函数是协程函数,调用协程函数返回协程对象
  2. await 可以处理可等待对象,可等待对象有协程对象(corotine)、任务对象(task,用于顶层操作,必须作用于协程对象)和未来对象(future,底层操作 task 的父类)
  3. 协程的核心是事件循环,目的是合理的调度可等待对象的执行逻辑从而优化io等待时间
  4. 事件循环的最小调度单元是任务,通过主动的将协程对象封装成的协程任务并注册到事件循环并通过内部的调度算法调度执行
  5. 运行协程函数需要使用特定的接口调用如 asyncio.run(main()),它会启动一个事件循环并将 main() 作为任务注册到事件循环中调度执行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/201122.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QNX下多窗口叠加融合方案

目的&#xff1a;QNX下EGL多窗口叠加融合方案 环境&#xff1a; 系统&#xff1a;QNX 环境&#xff1a;8155/8295问题&#xff1a; EGL有时候在同一个进程中因为引入不同的功能&#xff0c;在不同的线程中进行窗口的绘制和融合&#xff0c;QNX下的融合方案&#xff0c;实测使…

速记:一个保险丝检测电路

一个保险丝检测电路 保险丝熔断&#xff1a;红灯亮 保险丝正常&#xff1a;绿灯亮 同样的&#xff0c;仿真中的指示灯可以换成其他指示器件。

【MYSQL】表的基本查询

目录 前言 一、Create&#xff08;增&#xff09; 1.单行数据 全列插入 2.多行数据 指定列插入 3.插入否则更新 4.替换 二、Retrieve&#xff08;查&#xff09; 1.select列 1.1全列查询 1.2指定列查询 1.3查询字段为表达式 1.4为查询结果指定别名 1.5结果去重 …

Java 最全面试总结——3.多线程篇

1、说说Java中实现多线程有几种方法 创建线程的常用三种方式&#xff1a; 继承Thread类实现Runnable接口实现Callable接口&#xff08; JDK1.5> &#xff09;线程池方式创建 通过继承Thread类或者实现Runnable接口、Callable接口都可以实现多线程&#xff0c;不过实现Run…

Ebullient 硬件篇

一. 简介 哈喽&#xff0c;大家好&#xff0c;好久没有给大家分享新项目了&#xff0c;但之前分享了许多项目都没有认认真真的做完过&#xff0c;做到了一半&#xff0c;由于某些原因就放弃了&#xff0c;给自己的一种感觉是做了很多东西&#xff0c;但是能拿出来讲的缺没有几…

彩虹云商城搭建教程+源码程序

前言&#xff1a;域名服务器或宝塔主机商场程序在线云商城 随着电子商务的快速发展&#xff0c;越来越多的企业开始意识到开设一个自己的电子商城对于销售和品牌推广的重要性。然而&#xff0c;选择一家合适的网站搭建平台和正确地构建一个商城网站并不是一件容易的事情。本文…

AKConv:具有任意采样形状和任意数目参数的卷积核

文章目录 摘要1、引言2、相关工作3、方法3.1、定义初始采样位置3.2、可变卷积操作3.3、扩展AKConv3.3、扩展AKConv 4、实验4.1、在COCO2017上的目标检测实验4.2、在VOC 712上的目标检测实验4.3、在VisDrone-DET2021上的目标检测实验4.4、比较实验4.5、探索初始采样形状 5、分析…

【动态规划】LeetCode-70.爬楼梯

&#x1f388;算法那些事专栏说明&#xff1a;这是一个记录刷题日常的专栏&#xff0c;每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目&#xff0c;在这立下Flag&#x1f6a9; &#x1f3e0;个人主页&#xff1a;Jammingpro &#x1f4d5;专栏链接&…

C语言——深入理解指针(3)

目录 1. 字符指针 2. 数组指针 2.1 数组指针变量 2.2 数组指针变量的初始化 3.二维数组传参&#xff08;本质&#xff09; 4. 函数指针 4.1 函数指针变量的创建 4.2 函数指针的使用 4.3 typedef 5. 函数指针数组 6. 转移表&#xff08;函数指针数组的使用&#xff…

4G5G防爆执法记录仪、防爆智能安全帽赋能智慧燃气,可视化巡检巡线,安全生产管控

随着燃气使用的普及&#xff0c;燃气安全问题日益突出。传统应急安全问题处理方式暴露出以下问题&#xff1a; 应急预案不完善&#xff1a;目前一些燃气企业的应急预案存在实用性不高、流程不清晰等问题&#xff0c;导致在紧急情况下难以迅速启动和有效执行。 部门协同不流畅…

网工内推 | 中高级网工,IE认证优先,带薪年假,五险一金

01 敏于行&#xff08;北京&#xff09;科技有限公司 招聘岗位&#xff1a;高级网络开发工程师 职责描述&#xff1a; 1、负责设计、参与数字身份安全中网络安全模块相关项目&#xff08;零信任SDP、VPN等&#xff09;&#xff1b; 2、深入研究和理解网络底层协议和通信机制&…

【hacker送书第6期】深入理解Java核心技术

第6期图书推荐 内容简介作者简介精彩书评参与方式 内容简介 《深入理解Java核心技术&#xff1a;写给Java工程师的干货笔记&#xff08;基础篇&#xff09;》是《Java工程师成神之路》系列的第一本&#xff0c;主要聚焦于Java开发者必备的Java核心基础知识。全书共23章&#xf…

文件重命名:如何删除文件名中的下划线,特殊符号批量删除

在日常的工作中&#xff0c;经常会遇到文件名中包含特殊符号的情况&#xff0c;例如&#xff0c;一些文件名可能包含下划线、空格或其他特殊符号&#xff0c;这些符号可能会干扰我们的文件搜索和识别。此外&#xff0c;一些文件名可能包含无法识别的非标准字符&#xff0c;这可…

GeoServer改造Springboot源码四(图层管理设计)

一、界面设计 图 1图层管理列表 图 2选择图层数据源 图 3添加图层 图 4编辑图层

解决msvcr71.dll丢失5个方法,修复程序运行缺失dll问题

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcr71.dll丢失”。这个错误提示通常出现在运行某些程序或游戏时&#xff0c;给使用者带来了很大的困扰。那么&#xff0c;究竟是什么原因导致了msvcr71.dll文件的丢失呢&#xff1f;本文…

JS动态转盘可自由设置个数与概率

让我为大家介绍一下转盘的实现过程与原理&#xff0c;介绍都放在下面代码块中&#xff0c;一步一步的教会你。 我们转盘使用线段来实现 <!DOCTYPE html> <html> <head><meta charset"utf-8"><title></title><style type&quo…

Linux小程序之进度条

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;自己能实现进度条 > 毒鸡汤&#xff1a; > …

MySQL主从复制架构

MySQL主从复制架构 一、MySQL集群概述 ##1、集群的主要类型 高可用集群&#xff08;High Available Cluster&#xff0c;HA Cluster&#xff09; 高可用集群是指通过特殊的软件把独立的服务器连接起来&#xff0c;组成一个能够提供故障切换&#xff08;Fail Over&#xff09…

【Hadoop】集群资源管理器 YARN

一、yarn 简介 Apache YARN (Yet Another Resource Negotiator) 是 hadoop 2.x 引入的分布式资源管理系统。主要用于解决 hadoop 1.x 架构中集群资源管理和数据计算耦合在一起&#xff0c;导致维护成本越来越高的问题。 yarn主要负责管理集群中的CPU和内存 用户可以将各种服…

[ISCTF2023] Crypto/PWN/Reverse

最近新生赛还挺多&#xff0c;不过这个开始后注册页面就被删了&#xff0c;没注册上。拿别人的附件作了下。 Crypto 七七的欧拉 这题只给了n,e,c这种情况一般正常没法解&#xff0c;猜n不正常 import gmpy2 import libnum from crypto.Util.number import *flagbISCTF{****…