最新消息:20210917 已从crifan.com换到crifan.org

【已解决】Python中直接return数组和yield的区别

Python crifan 4837浏览 0评论

看到别人代码:

<code>def mongoGridfsFiles():
    ...
    client = MongoClient(uri)
    db = client.gridfs
    collection = db.fs.files
    for document in collection.find():
        yield document

</code>

def main():

    db = pymysql.connect(**curMysqlConfigDict)

    

    documents = mongoGridfsFiles()

    insertMedia(db, documents)

不太清楚:

为何没有直接用return返回整个数组,而要用yield

且在调试期间注意到:

mongoGridfsFiles没有先执行,而是在后续函数insertMedia中调用到了返回的每个document后,mongoGridfsFiles才执行,才开始产生一个个document并返回

搜:

python return vs yield

When to use yield instead of return in Python? – GeeksforGeeks

3. (译)Python关键字yield的解释(stackoverflow) — 一起写Python文章,一起看Python文章

彻底理解Python中的yield – 简书

What is the difference between yield and return in python? – Quora

Python 中的黑暗角落(一):理解 yield 关键字 | 始终

python – What does the “yield” keyword do? – Stack Overflow

想要理解yield,需要先理解generators

以及先要搞清楚:iterables

<code>&gt;&gt;&gt; mylist = [1, 2, 3]
&gt;&gt;&gt; for i in mylist:
...    print(i)
1
2
3
</code>

以及:

<code>&gt;&gt;&gt; mylist = [x*x for x in range(3)]
&gt;&gt;&gt; for i in mylist:
...    print(i)
0
1
4
</code>

类似的这种iterable的,比如

lists, strings, files…

等等,缺点是:

所有的值,都要保存到内存中

-》而实际情况下,当数据个数很多时,数据量很大时,我们并不希望所有的值都作为list返回,都放在内存中

Generators是个iterators,是其中一种iterable,你可以每次循环只返回一个值

Generators并不把所有的值都保存在内存中 -》 是实时的,在你用的时候,才产生,生成对应的值:

<code>&gt;&gt;&gt; mygenerator = (x*x for x in range(3))
&gt;&gt;&gt; for i in mygenerator:
...    print(i)
0
1
4
</code>

注意到此处是(),表示Generator

(之前的[],表示list)

不过Generator没法被(第二次)多次调用,即此处不能再次调用:

for i in mygenerator:

yield

yield,使用方式和return差不多

除了函数本身会返回一个generator

<code>&gt;&gt;&gt; def createGenerator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
&gt;&gt;&gt; mygenerator = createGenerator() # create a generator
&gt;&gt;&gt; print(mygenerator) # mygenerator is an object!
&lt;generator object createGenerator at 0xb7555c34&gt;
&gt;&gt;&gt; for i in mygenerator:
...     print(i)
0
1
4
</code>

此处去调试代码,发现的确函数mongoDialogs返回的的确是generator的object:

<code>&lt;generator object mongoDialogs at 0x10698dd00&gt;
</code>

所以,此处通过函数中用yield返回一个generator,满足了如下场景:

  • 确定会返回大量的,很多个的数据

  • 且不希望占用内存

    • 否则直接return返回数据的list,数据量太大,会占用太多内存

  • 且确保只会使用一次

    • generator只有一条命 -》generator只能用一次,第二次再去调用就无效了

  • 注意:直接调用对应包含了yield的函数时,函数是不会执行的,直到你后期使用到了该(函数返回的)generator时,函数才会真正执行

    • 逻辑是,感觉有点点像是delay的逻辑,延迟加载/执行

然后对于后续回复中,有人说的更清楚:

yield没有看起来的那么魔性。

其内部执行逻辑是:

你调用了包含yield的函数时,函数代码没有被执行,只是返回一个generator对象。

当后续你使用generator,去获取一个值时,python解析器才,第一次的,真正的去执行对应的代码,知道碰到第一个yield,返回你要的第一个值,然后暂停该函数的执行(等待下次继续被调用)

后续要返回第二个值时,python解析器再去继续恢复执行yield部分,返回第二个值,再暂停执行;

如此继续。

直到返回所有的值。

此时generator本身也就被消耗殆尽,彻底消失了。

如此而已。

也因此,generator只有一条命,无法被多次重复调用。

【总结】

  • 正常的,小批量的数据,直接用普通的list等类型,即可

    • list等类型的数据是保存在内存中的

  • 如果满足如下场景

    • 确定会返回大量的,很多个的数据

    • 且不希望占用太多内存

    • 且确保只会使用一次

      • generator只有一条命 -》generator只能用一次,第二次再去调用就无效了

  • 举例:

    • 比如我此处mongo中dialog有4万多个

    • 如果全部一次性返回成list列表,则会消耗大量内存

    • 且确保只会调用一次generator,用完就无需再用了

  • 再去使用yield:

    • 且确保你了解yield的内部工作逻辑

    • 注意事项:直接调用对应包含了yield的函数时,函数是不会执行的

      • 直到你后期使用到了该(函数返回的)generator时,函数才会真正执行

        • 感觉逻辑点像是delay的逻辑,延迟加载/执行

转载请注明:在路上 » 【已解决】Python中直接return数组和yield的区别

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
89 queries in 0.189 seconds, using 20.04MB memory