什么是生成器以及如何使用它们

你是否曾经不得不处理一个庞大的数据集,以至于使你的机器内存不堪重负?或者你有一个复杂的函数,需要在每次调用时保持一个内部状态,但这个函数太小,没有理由创建自己的类。在这些情况下以及更多的情况下,生成器和 Python 的 yield 语句可以帮助我们。

在本文结束时,你会知道。

什么是生成器以及如何使用它们

如何创建生成器函数和表达式

Python 产量语句是如何工作的

如何在一个生成器函数中使用多个 Python yield语句

如何使用高级生成器方法

如何用多个生成器构建数据管道

如果你是一个初级或中级Python爱好者,并且你有兴趣学习如何以更加Pythonic的方式处理大型数据集,那么这就是你的教程。

点击下面的链接,你可以得到本教程中使用的数据集的副本。

下载数据集。点击这里下载本教程中要使用的数据集,以学习Python中的生成器和产量。

使用生成器 从PEP 255引入的生成器函数是一种特殊的函数,它返回一个懒惰的迭代器。这些是你可以像列表一样循环的对象。然而,与列表不同,懒惰迭代器不在内存中存储其内容。对于 Python 中的迭代器的概述,请看 Python "for" 循环 (确定的迭代) 。

现在你对生成器的作用有了一个粗略的概念,你可能想知道它们在运行时是什么样子的。让我们看一下两个例子。在第一个例子中,你将从鸟瞰角度看到生成器是如何工作的。然后,你将放大并更彻底地检查每个例子。

例子1: 读取大文件 生成器的一个常见用例是处理数据流或大文件,如CSV文件。这些文本文件通过使用逗号将数据分成几列。这种格式是分享数据的一种常见方式。现在,如果你想计算CSV文件中的行数怎么办?下面的代码块显示了计算这些行的一种方法。

csv_gen = csv_reader("some_csv.txt")
row_count = 0

for row in csv_gen:
    row_count += 1
  
print(f "行数是{row_count}")

看了这个例子,你可能希望csv_gen是一个列表。为了填充这个列表,csv_reader()打开一个文件并将其内容加载到csv_gen中。然后,程序在列表中进行迭代,并为每一行增加row_count。

这是一个合理的解释,但是如果文件非常大,这种设计还能工作吗?如果文件大于你的可用内存怎么办?为了回答这个问题,让我们假设csv_reader()只是打开文件并将其读入一个数组。

def csv_reader(file_name):
    file = open(file_name)
    result = file.read().split("\n")
    return result

这个函数打开了一个给定的文件,并使用file.read().split()将每一行作为一个单独的元素添加到一个列表中。如果你在前面看到的行计数代码块中使用这个版本的csv_reader(),那么你会得到以下输出。

回溯(最近一次调用)。

Traceback (most recent call last):
  File "ex1_naive.py", line 22, in <module>
    main()
  File "ex1_naive.py", line 13, in main
    csv_gen = csv_reader("file.txt")
  File "ex1_naive.py", line 6, in csv_reader
    result = file.read().split("\n")
MemoryError

内存错误

在这种情况下,open()返回一个生成器对象,你可以懒散地逐行迭代。然而,file.read().split()将所有内容一次性加载到内存中,导致MemoryError的出现。

在这种情况发生之前,你可能会注意到你的电脑慢到了极点。你甚至可能需要用键盘中断来杀死这个程序。那么,你如何处理这些巨大的数据文件呢?看看csv_reader()的一个新定义吧。


def csv_reader(file_name):
    for row in open(file_name, "r"):
        yield row

在这个版本中,你打开文件,遍历它,并产生一行。这段代码应该产生以下输出,没有内存错误。

行数是64186394

这里发生了什么?嗯,你基本上把csv_reader()变成了一个生成器函数。这个版本打开一个文件,循环浏览每一行,并产生每一行,而不是返回它。

你还可以定义一个生成器表达式(也叫生成器理解),它的语法与列表理解非常相似。通过这种方式,你可以在不调用函数的情况下使用生成器。

csv_gen = (row for row in open(file_name))

这是一种更简洁的创建列表 csv_gen 的方法。你很快就会学到更多关于Python yield语句的知识。现在,只需记住这个关键的区别。

使用 yield 会产生一个生成器对象。

使用 return 将只产生文件的第一行。

例 2: 生成一个无限的序列

让我们换个角度,看看无限序列的生成。在 Python 中,为了得到一个有限序列,你可以调用 range() 并在一个列表上下文中评估它。

>> a = range(5)
>> list(a)
[0, 1, 2, 3, 4]

然而,生成一个无限序列需要使用一个生成器,因为你的计算机内存是有限的。


def infinite_sequence():
    num = 0
    while True:
        yield num
        num += 1

这个代码块短小精悍。首先,你初始化变量num并开始一个无限循环。然后,你立即产生num,这样你就可以捕捉到初始状态。这模拟了range()的动作。

如果你用for循环试试,你会发现它确实看起来是无限的。

>> for i in infinite_sequence():
... print(i, end=" ")
...
0 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
30 31 32 33 34 35 36 37 38 39 40 41 42
[...]
6157818 6157819 6157820 6157821 6157822 
6157823 6157824 6157825 6157826 6157827
6157828 6157829 6157830 6157831 6157832 
6157833 6157834 6157835 6157836 6157837
6157838 6157839 6157840 6157841 6157842

键盘中断 回溯(最近一次调用)。 文件" ",第2行,在 中 该程序将继续执行,直到你手动停止它。

你也可以不使用for循环,而直接在生成器对象上调用next()。这对于在控制台中测试一个生成器特别有用。


>>> gen = infinite_sequence()
>>> next(gen)
0
>>> next(gen)
1
>>> next(gen)
2
>>> 下一个(gen)
3

在这里,你有一个叫做gen的生成器,你通过重复调用next()来手动迭代它。这可以作为一个很好的理智检查,确保你的生成器产生你期望的输出。

注意:当你使用 next() 时,Python 对你作为参数传入的函数调用 .next() 。这种参数化允许有一些特殊的效果,但它超出了本文的范围。实验一下改变你传递给 next() 的参数,看看会发生什么。

例子3:检测对称回文

你可以在很多方面使用无限序列,但它们的一个实际用途是建立回文检测器。一个复数检测器会找出所有属于复数的字母或数字的序列。这些是正反两方面读起来相同的单词或数字,就像121。首先,定义你的数字检测器。


def is_palindrome(num):
    # 跳过个位数的输入
    if num // 10 == 0:
        return False
    temp = num
    reversed_num = 0

    while temp != 0:
        reversed_num = (reversed_num * 10) + (temp % 10)
        temp = temp // 10

    if num == reversed_num:
        return num
    else:
        return False

不要太担心理解这段代码中的基础数学。只要注意这个函数接收一个输入数字,将其反转,并检查反转的数字是否与原始数字相同。现在你可以使用你的无限序列生成器来得到一个所有数字重合体的运行列表。

>> for i in infinite_sequence():
... pal = is_palindrome(i)
... 如果pal:
... print(i)
...
11
22
33
[...]
99799
99899
99999
100001
101101
102201

键盘中断 回溯(最近一次调用)。 文件" ",第2行,在 中 文件 " ", 第5行, in is_palindrome 在这种情况下,只有那些向前或向后相同的数字才会被打印到控制台。

注意:在实践中,你不太可能自己编写无限序列发生器。itertools模块用itertools.count()提供了一个非常有效的无限序列发生器。

现在你已经看到了无限序列生成器的一个简单用例,让我们更深入地了解生成器的工作原理。

了解生成器

到目前为止,你已经了解了创建生成器的两种主要方式:通过使用生成器函数和生成器表达式。你甚至可能对生成器的工作原理有了直观的了解。让我们花点时间把这些知识变得更加明确。

生成器函数的外观和行为与普通函数一样,但有一个决定性的特征。生成器函数使用 Python 的 yield 关键字,而不是 return。回顾一下你之前写的生成器函数。

def infinite_sequence():
    num = 0
    while True:
        yield num
        num += 1

这看起来像一个典型的函数定义,除了 Python 的 yield 语句和它后面的代码。 yield 表示将一个值送回给调用者,但与 return 不同,你不会在之后退出函数。

相反,函数的状态被记住了。这样,当在生成器对象上调用next()时(无论是显式还是隐式在for循环中),先前产生的变量num被增加,然后再次产生。由于生成器函数看起来像其他函数,并且与它们的行为非常相似,你可以认为生成器表达式与 Python 中的其他理解式非常相似。

注意:你对 Python 的 list、set 和 dictionary 理解力感到生疏吗?你可以看看《有效地使用列表理解》。

用生成器表达式构建生成器

像列表理解一样,生成器表达式允许你只用几行代码就能快速创建一个生成器对象。它们在使用列表理解的情况下也很有用,而且还有一个额外的好处:你可以在迭代前不建立和保持整个对象在内存中的情况下创建它们。换句话说,当你使用生成器表达式时,你不会有任何内存损失。以这个对一些数字进行平方运算的例子为例。

>>> nums_squared_lc = [num**2 for num in range(5)] 
>> nums_squared_gc = (num**2 for num in range(5))

nums_squared_lc和nums_squared_gc看起来基本相同,但有一个关键的区别。你能发现它吗?看看当你检查这些对象的时候会发生什么。

>>> nums_squared_lc
[0, 1, 4, 9, 16]
>>> nums_squared_gc

<生成器对象<genexpr>在0x107fbbc78>

第一个对象使用括号建立了一个列表,而第二个对象通过使用小括号创建了一个生成器表达式。输出结果证实了你已经创建了一个生成器对象,并且它与一个列表不同。

剖析生成器的性能

你在前面了解到,生成器是优化内存的一个好方法。虽然无限序列生成器是这种优化的一个极端例子,但让我们放大你刚才看到的数字平方的例子,并检查产生的对象的大小。你可以通过调用sys.getsizeof()来做到这一点。

>>> import sys
>>> nums_squared_lc = [i ** 2 for i in range(10000)]
>>> sys.getsizeof(nums_squared_lc)
87624
>>> nums_squared_gc = (i ** 2 for i in range(10000))
>>> print(sys.getsizeof(nums_squared_gc))
120

在这种情况下,你从列表理解中得到的列表是 87,624 字节,而生成器对象只有 120 个。这意味着列表比生成器对象大了700多倍!这就是为什么我们要把列表理解为 "生成器"。

不过,有一点需要注意的是。如果列表小于运行机器的可用内存,那么列表理解的评估速度就会比等价的生成器表达式快。为了探讨这个问题,让我们把上面两个理解式的结果相加。你可以用cProfile.run()生成一个读出结果。


>>> import cProfile
>>> cProfile.run('sum([i * 2 for i in range(10000)])')
         5 function calls in 0.001 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.001    0.001    0.001    0.001 <string>:1(<listcomp>)
        1    0.000    0.000    0.001    0.001 <string>:1(<module>)
        1    0.000    0.000    0.001    0.001 {built-in method builtins.exec}
        1    0.000    0.000    0.000    0.000 {built-in method builtins.sum}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


>>> cProfile.run('sum((i * 2 for i in range(10000)))')
         10005 function calls in 0.003 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    10001    0.002    0.000    0.002    0.000 <string>:1(<genexpr>)
        1    0.000    0.000    0.003    0.003 <string>:1(<module>)
        1    0.000    0.000    0.003    0.003 {built-in method builtins.exec}
        1    0.001    0.001    0.003    0.003 {built-in method builtins.sum}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

在这里,你可以看到,在列表理解中的所有值中求和所花费的时间大约是在生成器中求和的三分之一。如果速度是一个问题,而内存不是问题,那么列表理解可能是一个更好的工具来完成这项工作。

注意:这些测量结果不仅对使用生成器表达式的对象有效。它们对于用类似的生成器函数生成的对象也是一样的,因为所产生的生成器是等价的。

记住,列表理解返回完整的列表,而生成器表达式返回生成器。无论生成器是由函数还是由表达式构建,其工作原理都是一样的。使用表达式只是允许你在一行中定义简单的生成器,并在每个内部迭代结束时假定有一个 yield。

Python 的 yield 语句无疑是生成器所有功能的关键所在,所以让我们深入了解 yield 在 Python 中是如何工作的。

理解Python的yield语句

总的来说,yield 是一个相当简单的语句。它的主要工作是以一种类似于返回语句的方式来控制生成器函数的流程。正如上面简单提到的,尽管如此,Python 的 yield 语句还是有一些小技巧的。

当你调用一个生成器函数或使用一个生成器表达式时,你会返回一个特殊的迭代器,称为生成器。你可以把这个生成器分配给一个变量,以便使用它。当你调用生成器上的特殊方法时,比如 next(),函数中的代码会被执行到 yield。

当Python的yield语句被击中时,程序会暂停函数的执行并将yield的值返回给调用者。(相比之下,return则完全停止了函数的执行。) 当一个函数被暂停时,该函数的状态被保存。这包括生成器本地的任何变量绑定,指令指针,内部堆栈,以及任何异常处理。

这样,只要你调用生成器的一个方法,就可以恢复函数的执行。这样一来,所有的函数评估都会在屈服后重新开始。你可以通过使用多个Python屈服语句看到这个动作。

>>> def multi_yield():
...     yield_str = "This will print the first string"
...     yield yield_str
...     yield_str = "This will print the second string"
...     yield yield_str
...
>>> multi_obj = multi_yield()
>>> print(next(multi_obj))
This will print the first string
>>> print(next(multi_obj))
This will print the second string
>>> print(next(multi_obj))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

仔细看看最后一次对 next() 的调用。你可以看到,执行过程中出现了一个回溯。这是因为生成器,像所有的迭代器一样,可以被耗尽。除非你的生成器是无限的,否则你只能对它进行一次迭代。一旦所有的值都被评估过,迭代就会停止,for循环就会退出。如果你使用了 next() ,那么反而会得到一个明确的 StopIteration 异常。

注意:StopIteration是一个自然的异常,它被提出来作为迭代器结束的信号。例如,for 循环就是围绕StopIteration建立的。你甚至可以通过使用while循环来实现你自己的for循环。


>>> letters = ["a""b""c""y"]
>>> it = iter(letters)
>>> while True:
...     try:
...         letter = next(it)
...     except StopIteration:
...         break
...     print(letter)
...
a
b
c
y

你可以在关于异常的 Python 文档中阅读更多关于 StopIteration 的内容。关于一般迭代的更多信息,请查看 Python "for" 循环 (定值迭代) 和 Python "while" 循环 (不定值迭代) 。

yield可以用很多方式来控制生成器的执行流程。在你的创造力允许的范围内,可以利用多个Python yield语句。

使用高级生成器方法

你已经看到了生成器最常见的用法和结构,但还有一些技巧需要介绍。除了屈服之外,生成器对象还可以使用以下方法。

.send()
.throw()
.close()

如何使用.send() 在下一节中,你将建立一个利用所有三种方法的程序。这个程序将像以前一样打印数字回文,但有一些调整。当遇到一个复数时,你的新程序将添加一个数字,并从那里开始搜索下一个数字。你还会用.throw()来处理异常,并在给定的数字数量后用.close()来停止生成器。首先,让我们回顾一下你的回文检测器的代码。


def is_palindrome(num):
    # Skip single-digit inputs
    if num // 10 == 0:
        return False
    temp = num
    reversed_num = 0

    while temp != 0:
        reversed_num = (reversed_num * 10) + (temp % 10)
        temp = temp // 10

    if num == reversed_num:
        return True
    else:
        return False

这和你之前看到的代码是一样的,只是现在程序严格返回True或False。你还需要修改你原来的无限序列发生器,像这样。

def infinite_palindromes():
    num = 0
    while True:
        if is_palindrome(num):
            i = (yield num)
            if i is not None:
                num = i
        num += 1

这里有很多变化! 你将看到的第一个变化是在第5行,i = (yield num)。尽管你在前面学到了 yield 是一个语句,但这并不是故事的全部。

从 Python 2.5 (也就是引入你现在正在学习的方法的那个版本) 开始, yield 是一个表达式,而不是一个语句。当然,你仍然可以把它作为一个语句来使用。但是现在,你也可以像你在上面的代码块中看到的那样使用它,在这里,i 取出了被产生的值。这样你就可以对产生的值进行操作。更重要的是,它允许你向生成器发送()一个值。当屈服后继续执行时,i将获取所发送的值。

你还会检查i是否为None,如果在生成器对象上调用next(),就会发生这种情况。(如果i有一个值,那么你就用新的值更新num。但是不管i是否有值,你都要增加num并再次开始循环。

现在,看看主函数代码,它将最低的数字与另一个数字一起发回给生成器。例如,如果回文是121,那么它将.send() 1000

pal_gen = infinite_palindromes()
for i in pal_gen:
    digits = len(str(i))
    pal_gen.send(10 ** (digits))

通过这段代码,你创建了生成器对象并对其进行了迭代。该程序只有在找到一个回文的时候才会产生一个值。它使用len()来确定该数字数量。然后,它向生成器发送10**个数字。由于i现在有了一个值,程序就会更新num,增量,并再次检查复数。

一旦你的代码找到并产生了另一个重合体,你将通过for循环进行迭代。这与用next()进行迭代是一样的。生成器也是在第5行用i = (yield num)来接续。然而,现在i是无,因为你没有明确地发送一个值。

你在这里创建的是一个coroutine,或者说是一个生成器函数,你可以将数据传入其中。这些对于构建数据管道是很有用的,但正如你很快就会看到的,它们对于构建数据管道并不是必须的。(如果你想深入了解,那么这门关于程序和并发性的课程是最全面的处理方法之一)。

现在你已经了解了.send(),让我们看一下.throw()

如何使用 .throw()

.throw()允许你用生成器抛出异常。在下面的例子中,你在第6行提出异常。一旦数字达到5,这段代码将抛出一个ValueError。

pal_gen = infinite_palindromes()
for i in pal_gen:
    print(i)
    digits = len(str(i))
    if digits == 5:
        pal_gen.throw(ValueError("We don't like large palindromes"))
    pal_gen.send(10 ** (digits))

这与前面的代码相同,但现在你要检查digits是否等于5,如果是,那么你将.throw()一个ValueError。为了确认这和预期的一样,看一下代码的输出。

11
111
1111
10101
回溯(最近的一次调用)。
  File "advanced_gen.py", line 47, in <module>.
    main()
  文件 "advanced_gen.py", 第41行, in main
    pal_gen.throw(ValueError("We don't like large palindromes"))
  文件 "advanced_gen.py", 第26行, in infinite_palindromes
    i = (yield num)
ValueError
  

我们不喜欢大的回文。.throw()在任何你可能需要捕捉异常的地方都很有用。在这个例子中,你用.throw()来控制你何时停止对生成器的迭代。

你可以用.close()更优雅地做到这一点。

如何使用 .close()

顾名思义,.close()允许你停止一个生成器。这在控制无限序列发生器时特别方便。让我们更新上面的代码,将.throw()改为.close()来停止迭代。

pal_gen = infinite_palindromes()
for i in pal_gen:
    print(i)
    digits = len(str(i))
    if digits == 5:
        pal_gen.close()
    pal_gen.send(10 ** (digits))

你没有调用.throw(),而是在第6行使用.close()。使用.close()的好处是它会引发StopIteration,这是一个用来表示有限迭代器结束的异常。

11
111
1111
10101
#回溯(最近的一次调用)
Traceback (most recent call last):
  File "advanced_gen.py", line 46, in <module>
    main()
  File "advanced_gen.py", line 42, in main
    pal_gen.send(10 ** (digits))
StopIteration
#停止迭代

现在你已经了解了更多关于生成器的特殊方法,让我们来谈谈使用生成器来建立数据管道的问题。

用生成器创建数据管线

数据管道允许你将代码串联起来,以处理大型数据集或数据流,而不会耗尽机器的内存。想象一下,你有一个大的CSV文件。


固定地址,公司,员工人数,类别,城市,州,融资日期,融资金额,融资币种,回合数
digg,Digg,60,web,San Francisco,CA,1-Dec-06,8500000,USD,b
digg,Digg,60,web,San Francisco,CA,1-Oct-05,2800000,USD,a
facebook,Facebook,450,web,Palo Alto,CA,1-Sep-04,500000,USD,angel
Facebook,Facebook,450,web,Palo Alto,CA,1-May-05,12700000,USD,a
photobucket,Photobucket,60,web,Palo Alto,CA,1-Mar-05,3000000,USD,a

这个例子来自TechCrunch美国大陆集,它描述了位于美国的各种初创公司的融资回合和美元数额。点击下面的链接下载该数据集。

下载数据集。点击这里下载数据集,你将在本教程中使用它来学习Python中的生成器和产量。

现在是在Python中进行处理的时候了! 为了演示如何用生成器建立管道,你要分析这个文件,以获得数据集中所有A轮系列的总数和平均值。

让我们想一个策略。

读取文件的每一行。

把每一行分成一个值的列表。

提取列名。

使用列名和列表来创建一个字典。

过滤掉你不感兴趣的回合。

计算你感兴趣的轮次的总数和平均值。

通常情况下,你可以用像pandas这样的包来做这件事,但你也可以只用几个生成器来实现这个功能。你首先要用生成器表达式从文件中读取每一行。


file_name = "techcrunch.csv"
lines = (line for line in open(file_name))

然后,你将使用另一个生成器表达式与前一个表达式配合,将每一行分割成一个列表。

list_line = (s.rstrip().split(","for s in lines)

在这里,你创建了生成器list_line,它迭代了第一个生成器行。这是设计生成器流水线时常用的模式。接下来,你将从techcrunch.csv中提取列名。由于列名往往是CSV文件中的第一行,你可以用一个简短的

next()

调用来获取。

cols = next(list_line)

这个对next()的调用使迭代器在list_line生成器上前进了一次。把这一切放在一起,你的代码应该是这样的。

file_name = "techcrunch.csv"
lines = (line for line in open(file_name))
list_line = (s.rstrip().split(","for s in lines)
cols = next(list_line)

总结一下,你首先创建了一个表达式生成器lines来产生文件中的每一行。接下来,你在另一个叫做list_line的生成器表达式的定义中迭代该生成器,它将每一行变成一个值的列表。然后,你用 next() 将 list_line 的迭代推进一次,以获得 CSV 文件中的列名列表。

注意:注意尾部的新行! 这段代码利用了 list_line 生成器表达式中的 .rstrip() 来确保没有尾部换行符,这可能存在于 CSV 文件中。

为了帮助你过滤和执行对数据的操作,你将创建字典,其中键是CSV中的列名。

c

ompany_dicts = (dict(zip(cols, data)) for data in list_line)

这个生成器表达式遍历由list_line产生的列表。然后,它使用 zip()dict() 来创建上面指定的 dictionary。现在,你将使用第四个生成器来过滤你想要的融资回合,并同时拉出 raisedAmt。

funding = (
    int(company_dict["raisedAmt"])
    for company_dict in company_dicts
    if company_dict["round"] == "a"
)

在这段代码中,你的生成器表达式遍历了company_dict的结果,并对round键为 "a "的任何company_dict提取了raisedAmt。

请记住,在生成器表达式中,你并没有一次遍历所有这些结果。事实上,在你真正使用for循环或者像sum()这样对迭代变量起作用的函数之前,你并没有迭代任何东西。事实上,现在调用sum()来迭代生成器。

total_series_a = sum(funding)

把这一切放在一起,你会产生以下脚本。

total_series_a = sum(funding)
file_name = "techcrunch.csv"
lines = (line for line in open(file_name))
list_line = (s.rstrip()split(","for s in lines)
cols = next(list_line)
company_dicts = (dict(zip(cols, data)) for data in list_line)
funding = (
    int(company_dict["raisedAmt"])
    for company_dict in company_dicts
    if company_dict["round"] == "a"
)
total_series_a = sum(funding)
print(f"Total series A fundraising: ${total_series_a}")

这个脚本将你建立的每一个生成器拉到一起,它们都作为一个大数据管道发挥作用。下面是一个逐行的分解。

第2行读入文件的每一行。

第3行将每一行分割成数值,并将这些数值放入一个列表。

第4行使用next()将列名存储在一个列表中。

第5行创建字典,并用zip()调用将其合并。

键是第4行的列名cols。

值是在第3行中创建的列表形式的行。

第6行获得每个公司的A系列融资金额。它还过滤掉了任何其他的融资金额。

第11行通过调用sum()来获得CSV中发现的A轮融资总额,从而开始了迭代过程。

当你在techcrunch.csv上运行这段代码时,你应该发现A轮融资的总额为437.6015万美元。

注意:本教程中开发的处理CSV文件的方法对于理解如何使用生成器和Python的yield语句非常重要。然而,当你在 Python 中处理 CSV 文件时,你应该使用包含在 Python 标准库中的 csv 模块。这个模块有优化的方法来有效地处理 CSV 文件。

为了更深入地挖掘,试着计算出每家公司在A轮融资中的平均融资额。这有点棘手,所以这里有一些提示。

生成器在被完全迭代后会自我耗尽。 你仍然需要sum()函数。 祝您好运!

总结 在本教程中,你已经了解了生成器函数和生成器表达式。

你现在知道了。

如何使用和编写生成器函数和生成器表达式 重要的 Python yield 语句如何使生成器发挥作用 如何在一个生成器函数中使用多个 Python 屈服语句 如何使用.send()来向生成器发送数据 如何使用.throw()来引发生成器的异常 如何使用.close()来停止一个生成器的迭代 如何建立一个生成器流水线来有效地处理大型CSV文件 你可以通过以下链接获得本教程中使用的数据集。

下载数据集。点击这里下载你在本教程中使用的数据集,学习Python中的生成器和产量。

生成器在你的工作或项目中是如何帮助你的?如果你只是在学习它们,那么你打算在未来如何使用它们?你是否找到了解决数据管道问题的好办法?请在下面的评论中告诉我们!

本文由 mdnice 多平台发布

猜你喜欢

转载自blog.csdn.net/qq_40523298/article/details/127498907