龙空技术网

七爪源码:Python Up 你的代码:生成器

庄志炎 49

前言:

今天兄弟们对“正则表达式生成器”大体比较着重,我们都需要学习一些“正则表达式生成器”的相关资讯。那么小编也在网摘上搜集了一些关于“正则表达式生成器””的相关知识,希望朋友们能喜欢,大家快快来了解一下吧!

Python 中的生成器代表什么,它们的用法和最佳实践

让我们指出,通过这些生成器,我们可以用更少的努力来实现所有这些功能。我想我可以描述生成器的最简单的方式是:一个返回生成器对象的函数,然后我们可以在迭代过程中使用它。

现在,生成器只不过是一个函数,创建它相当容易。但与通常使用 return 语句的常规函数​相反,它确实有使用 yield 语句的扭曲。

就返回的值而言,yield 和 return 的作用相同。它们返回值。不同之处在于这两个对函数执行流程的影响:

return 终止函数的执行;yield 就像音乐播放器上的暂停按钮。它维护函数的状态,以便稍后它可以从该点恢复,如果再次调用它。

生成器函数与常规函数有一些基本区别:

生成器函数包含一个或多个 yield 语句。常规函数不包含此类语句。如果是这样,它将立即成为生成器函数;作为生成器函数,它返回一个生成器对象。但是,调用该函数不会开始执行。我们可以使用 next() 函数或 for / while 循环对其进行迭代,然后生成器函数才开始执行;像 __iter__() 和 __next__() 这样的方法是自动实现的。因此,我们可以使用 next() 遍历元素;如前所述,yield 只是暂停函数并将控制权转移到调用块;这意味着局部变量及其值在连续调用之间保持不变;当函数终止时,StopIteration 会在进一步调用时自动引发。这也意味着迭代器只能迭代一次。要重新启动该过程,我们需要创建另一个生成器对象;

让我们举例说明所有这些功能:

# define a generator function to generate 1 numberdef my_generator():    print("Generator function running")    n = 1    yield n# run the function to return a generator objectmy_gen = my_generator()print(f"{type(my_gen)}: {my_gen}")# only when we iterate over it,# does the generator function execute# ask for two values, but generator can only give 1print(next(my_gen))print(next(my_gen))Output:<class 'generator'>: <generator object my_generator at 0x7fbed6d56490>Generator function running1Traceback (most recent call last):  ...    print(next(my_gen))StopIteration

正如我们所见,在这种情况下,生成器函数只包含一个 yield 语句。

我们还看到,我们可以使用 next() 函数迭代生成器函数返回的迭代器。 这意味着迭代器协议方法(__iter__() 和 __next__())都已经实现;

输出行非常清楚地表明生成器函数在我们对 next() 函数的两次调用触发之前不会开始实际执行。 另外,请注意对 next() 的第二次调用如何触发 StopIteration 异常。

让我们举一个更现实的例子。 如果生成器所做的只是生成一个单一的值,那么它们将不会那么有用:

# define a generator functiondef my_generator():    print("Generator function called")    for element in range(10):        yield element# run the function to return a generator objectmy_gen = my_generator()# use the generator objectfor number in my_gen:    print(number)Output:0123456789

生成器的另一个非常有趣的特性是,我们不仅可以从它们那里获取数据,还可以向它们提供数据,可能会即时自定义它们的行为。 这是一个简单的用例。 想象一下我们需要一个生成器函数来返回一个数字的三元组值的情况:

# define a generator functiondef my_generator():    while True:        # get the value        value = yield        # triple it and return it        yield value * 3# now use the generatormy_gen = my_generator()for number in range(10):    next(my_gen)    print(my_gen.send(number))Output:0369121518212427

看看我们使用 yield 关键字让生成器函数获取我们通过 send() 发送给它的值是多么容易? 这是 yield 关键字的另一种用途。 可能会有点令人困惑,因为我们已经习惯了 yield —— return 类比和 return 绝不是与输出相关的关键字,但是,值得注意的是,我刚刚介绍的 yield 关键字的两种用法都只与生成器有关 ,所以也有。

另外,请注意,尝试立即在尚未执行到已到达 yield 语句的生成器上调用 send() 是不允许的。 它会抛出一个 TypeError:

Traceback (most recent call last):  ...    my_gen.send(123)TypeError: can't send non-None value to a just-started generator

让我们深入了解一下 for 循环实现了什么以及它是如何工作的:

对 next() 的调用使生成器执行,直到到达第一个 yield 语句(对 next() 的调用等效于 send(None),因此我们可能已经使用 send(None) 而不是调用 next () 在发电机上);此时,生成器正在等待某个值发送给它;我们通过 send() 方法发送该值,然后生成器通过消耗发送的值恢复执行,然后生成三倍的值,并在生成新元素后再次暂停执行;循环重新开始,对 next() 的另一个调用将看到生成器再次恢复执行,直到生成器函数的第一个 yield 语句,在那里它将耐心等待下一个 send() 调用,然后循环继续 .

上面的代码可以重写为只使用一个 yield 表达式,方式如下:

# define a generator functiondef my_generator():    value = -1    while True:        # get the value and yield its triple        value = yield value * 3# create the generator# start its execution up to the yield statementmy_gen = my_generator()next(my_gen)for number in range(10):    print(my_gen.send(number))Output:0369121518212427
首先,我们调用生成器函数。它返回一个生成器对象,我们将其分配给 my_gen;然后,我们在它上面调用 next() 并且生成器函数开始执行,直到它到达 yield 表达式;send() 方法在生成器对象上调用。生成器函数获取该值,然后继续执行,直到再次到达 yield 语句;此时它返回三倍的值并再次暂停执行,等待下一个 send() 方法。

生成器表达式

如果我们的目标是构建相对简单的生成器,我们甚至不需要单独的生成器函数。我们可以使用生成器表达式。从语法上看,它看起来与列表推导非常相似。但是,有两个基本方面使它们与众不同:

与列表推导式不同,列表推导式可以通过包含推导式的方括号来识别,生成器表达式具有正则(圆)括号或圆括号;列表推导式生成整个列表,而生成器表达式则 - 生成元素。

让我们举例说明:

# define a listmy_list = [0, 1, 2, 3]# list comprehension: triple the value for each elementmy_list_comprehension = [i * 3 for i in my_list]# generator expression: same functionalitymy_generator = (i * 3 for i in my_list)print(my_list_comprehension)print(my_generator)Output:[0, 3, 6, 9]<generator object <genexpr> at 0x7f8a4446e340>

正如预期的那样,生成器表达式生成了一个生成器对象,该对象会根据需要一一生成结果,就像我们之前看到的生成器函数的情况一样。

发电机的优点

生成器相对于其他可迭代对象的一大优势是,与列表不同,例如,列表必须将所有元素存储在内存中,生成器只需要存储当前元素以及如何到达下一个元素。这使得生成器在内存消耗方面非常高效,并且还为生成器提供了能够表示真正无限的数据流的独特优势,特别是因为它们一次生成一个项目,这意味着只有一个元素将存储在内存中在任何给定时间。

生成器的另一个优点是,在我们开始在代码中进一步使用它们之前,我们不需要等待所有元素都生成(就像我们需要的那样,在列表的情况下)。

我们应该注意到,如果我们不打算多次使用生成的数字,那么这两个优点都是显而易见的并且有任何实际用途。

它们也被发现非常容易实现,与迭代器不同,迭代器具有相当多的样板代码来实现这两个 __iter__() 和 __next__() 方法。

总结这一点,我希望它有助于积累一些(更多)关于生成器的知识。本文绝不是有关该主题的详尽信息来源。互联网上充斥着关于 Python 生成器主题的非常详尽的教程和文档。

话虽如此,请注意安全,我们下一场再见!快乐编码!

关注七爪网,获取更多APP/小程序/网站源码资源!

标签: #正则表达式生成器 #python web项目开发代码生成器