python生成器实例_Python生成器

本篇文章帮大家学习python生成器,包含了Python生成器使用方法、操作技巧、实例演示和注意事项,有一定的学习价值,大家可以用来参考。

在本文中,将学习如何使用Python生成器来创建迭代,了解它与迭代器和常规函数有什么区别,以及为什么要使用它。

在Python中构建迭代器有很多开销; 必须使用__iter__()和__next__()方法实现一个类,跟踪内部状态,当没有值被返回时引发StopIteration异常。

Python生成器是创建迭代器的简单方法。上面提到的所有开销都由Python中的生成器自动处理。

简单来说,生成器是返回一个可以迭代的对象(迭代器)的函数(一次一个值)。

如何在Python中创建生成器?

在Python中创建生成器是相当简单的。 它使用yield语句而不是return语句来定义,与正常函数一样简单。

如果函数包含至少一个yield语句(它可能包含其他yield或return语句),那么它将成为一个生成器函数。 yield和return都将从函数返回一些值。

不同的是,return语句完全终止函数,但yield语句会暂停函数保存其所有状态,并在以后的连续调用中继续执行(有点像线程挂起的意思)。

生成器函数与正常函数的差异

下面列出的是生成器函数与正常函数的区别 -

生成器函数包含一个或多个yield语句。

当被调用时,它返回一个对象(迭代器),但不会立即开始执行。

__iter__()和__next__()之类的方法将自动实现。所以可以使用next()迭代项目。

一旦函数退让(yields),该函数将被暂停,并将该控制权交给调用者。

局部变量及其状态在连续调用之间被记住。

最后,当函数终止时,StopIteration会在进一步的调用时自动引发。

下面的例子用来说明上述所有要点。 我们有一个名为my_gen()的生成器函数和几个yield语句。

#!/usr/bin/python3

#coding=utf-8

def my_gen():

n = 1

print('This is printed first, n= ', n)

# Generator function contains yield statements

yield n

n += 1

print('This is printed second, n= ', n)

yield n

n += 1

print('This is printed at last, n= ', n)

yield n

下面给出了交互式运行结果。 在Python shell中运行它们以查看输出 -

>>> # It returns an object but does not start execution immediately.

>>> a = my_gen()

>>> # We can iterate through the items using next().

>>> next(a)

This is printed first, n = 1

1

>>> # Once the function yields, the function is paused and the control is transferred to the caller.

>>> # Local variables and theirs states are remembered between successive calls.

>>> next(a)

This is printed second, n = 2

2

>>> next(a)

This is printed at last, n = 3

3

>>> # Finally, when the function terminates, StopIteration is raised automatically on further calls.

>>> next(a)

Traceback (most recent call last):

...

StopIteration

>>> next(a)

Traceback (most recent call last):

...

StopIteration

在上面的例子中需要注意的是,在每个调用之间函数会保持住变量n的值。与正常函数不同,当函数产生时,局部变量不会被销毁。 此外,生成器对象只能重复一次。

要重新启动该过程,需要使用类似于a = my_gen()的方法创建另一个生成器对象。

注意:最后要注意的是,可以直接使用带有for循环的生成器。

这是因为,for循环需要一个迭代器,并使用next()函数进行迭代。 当StopIteration被引发时,它会自动结束。 请查看这里了解一个for循环是如何在Python中实际实现的。

# A simple generator function

def my_gen():

n = 1

print('This is printed first')

# Generator function contains yield statements

yield n

n += 1

print('This is printed second')

yield n

n += 1

print('This is printed at last')

yield n

# Using for loop

for item in my_gen():

print(item)

当运行程序时,将输出结果为:

This is printed first

1

This is printed second

2

This is printed at last

3

具有循环的Python生成器

上面的例子没有什么用,我们研究它只是为了了解在后台发生了什么。通常,生成器功能用具有适当终止条件的循环实现。

我们举一个反转字符串的生成器的例子 -

length = len(my_str)

for i in range(length - 1,-1,-1):

yield my_str[i]

# For loop to reverse the string

# Output:

# o

# l

# l

# e

# h

for char in rev_str("hello"):

print(char)def rev_str(my_str):

在这个例子中,使用range()函数使用for循环以相反的顺序获取索引。事实证明,这个生成函数不仅可以使用字符串,还可以使用其他类型的列表,元组等迭代。

Python生成器表达式

使用生成器表达式,可以轻松创建简单的生成器。 它使构建生成器变得容易。

与lambda函数一样创建一个匿名函数,生成器表达式创建一个匿名生成函数。生成器表达式的语法与Python中的列表解析类似。 但方圆[]替换为圆括号()。

列表推导和生成器表达式之间的主要区别是:列表推导产生整个列表,生成器表达式一次生成一个项目。

它们是处理方式是懒惰的,只有在被要求时才能生产项目。 因此,生成器表达式的存储器效率高于等效列表的值。

# Initialize the list

my_list = [1, 3, 6, 10]

# square each term using list comprehension

# Output: [1, 9, 36, 100]

[x**2 for x in my_list]

# same thing can be done using generator expression

# Output: at 0x0000000002EBDAF8>

(x**2 for x in my_list)

我们可以看到,生成器表达式没有立即生成所需的结果。 相反,它返回一个发生器对象,并根据需要生成项目。

# Intialize the list

my_list = [1, 3, 6, 10]

a = (x**2 for x in my_list)

# Output: 1

print(next(a))

# Output: 9

print(next(a))

# Output: 36

print(next(a))

# Output: 100

print(next(a))

# Output: StopIteration

next(a)

生成器表达式可以在函数内部使用。当以这种方式使用时,圆括号可以丢弃。

>>> sum(x**2 for x in my_list)

146

>>> max(x**2 for x in my_list)

100

为什么在Python中使用生成器?

有几个原因使得生成器成为有吸引力。

1. 容易实现

与其迭代器类相比,发生器可以以清晰简洁的方式实现。 以下是使用迭代器类来实现2的幂次序的例子。

class PowTwo:

def __init__(self, max = 0):

self.max = max

def __iter__(self):

self.n = 0

return self

def __next__(self):

if self.n > self.max:

raise StopIteration

result = 2 ** self.n

self.n += 1

return result

上面代码有点长,可以使用一个生成器函数实现同样的功能。

def PowTwoGen(max = 0):

n = 0

while n < max:

yield 2 ** n

n += 1

因为,生成器自动跟踪的细节,它更简洁,更干净。

2.内存高效

返回序列的正常函数将在返回结果之前会在内存中的创建整个序列。如果序列中的项目数量非常大,这可是要消耗内存的。

序列的生成器实现是内存友好的,并且是推荐使用的,因为它一次仅产生一个项目。

3. 表未无限流

生成器是表示无限数据流的绝佳媒介。 无限流不能存储在内存中,由于生成器一次只能生成一个项目,因此可以表示无限数据流。

以下示例可以生成所有偶数(至少在理论上)。

def all_even():

n = 0

while True:

yield n

n += 2

4.管道生成器

生成器可用于管理一系列操作,下面使用一个例子说明。

假设我们有一个快餐连锁店的日志文件。 日志文件有一列(第4列),用于跟踪每小时销售的比萨饼数量,我们想算出在5年内销售的总萨饼数量。

假设一切都是字符串,不可用的数字标记为“N / A”。 这样做的生成器实现可以如下。

with open('sells.log') as file:

pizza_col = (line[3] for line in file)

per_hour = (int(x) for x in pizza_col if x != 'N/A')

print("Total pizzas sold = ",sum(per_hour))

这种管道的方式是更高效和易于阅读的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值