您可以使用Python生成器函数做什么?
我开始学习Python,我遇到了生成器函数,这些函数中有一个yield语句。我想知道这些函数真正擅长解决哪些类型的问题。请参阅中的“动机”部分您可以使用Python生成器函数做什么?,python,generator,Python,Generator,我开始学习Python,我遇到了生成器函数,这些函数中有一个yield语句。我想知道这些函数真正擅长解决哪些类型的问题。请参阅中的“动机”部分 生成器的一个不明显的用途是创建可中断函数,它允许您在不使用线程的情况下“同时”(实际上是交错)执行更新UI或运行多个作业等操作。生成器为您提供延迟评估。您可以通过对它们进行迭代来使用它们,可以显式地使用“for”,也可以隐式地将其传递给任何进行迭代的函数或构造。您可以将生成器视为返回多个项目,就像它们返回一个列表一样,但它们不是一次返回所有项目,而是逐个
生成器的一个不明显的用途是创建可中断函数,它允许您在不使用线程的情况下“同时”(实际上是交错)执行更新UI或运行多个作业等操作。生成器为您提供延迟评估。您可以通过对它们进行迭代来使用它们,可以显式地使用“for”,也可以隐式地将其传递给任何进行迭代的函数或构造。您可以将生成器视为返回多个项目,就像它们返回一个列表一样,但它们不是一次返回所有项目,而是逐个返回,并且生成器函数将暂停,直到请求下一个项目 生成器适用于计算大型结果集(特别是涉及循环本身的计算),您不知道是否需要所有结果,或者不希望同时为所有结果分配内存。或者对于生成器使用另一个生成器或消耗其他资源的情况,如果发生得越晚越好 生成器的另一个用途(实际上是相同的)是用迭代替换回调。在某些情况下,您希望函数执行大量工作,并偶尔向调用方报告。传统上,您会为此使用回调函数。将此回调传递给work函数,它将定期调用此回调。生成器方法是,功函数(现在是生成器)对回调一无所知,只在需要报告某个内容时生成。调用者没有编写单独的回调并将其传递给work函数,而是在生成器周围的一个小“for”循环中完成所有报告工作 例如,假设您编写了一个“文件系统搜索”程序。您可以完整地执行搜索,收集结果,然后一次显示一个结果。在显示第一个结果之前,必须收集所有结果,并且所有结果将同时保存在内存中。或者,您可以在查找结果时显示结果,这将更节省内存,对用户更友好。后者可以通过将结果打印函数传递给文件系统搜索函数来实现,也可以通过将搜索函数设置为生成器并迭代结果来实现 如果您想查看后两种方法的示例,请参阅os.path.walk()(带回调的旧文件系统遍历函数)和os.walk()(新的文件系统遍历生成器)。当然,如果您真的想将所有结果收集到一个列表中,那么生成器方法很容易转换为大列表方法:
big_list = list(the_generator)
基本上避免了在输入维护状态上迭代时的回调函数
有关使用generator可以做什么的概述,请参见和。使用generator的原因之一是为了使某些解决方案的解决方案更清晰 另一种方法是一次只处理一个结果,避免建立大量的结果列表,而这些结果无论如何都要分开处理 如果您有一个fibonacci-up-to-n函数,如下所示:
# function version
def fibon(n):
a = b = 1
result = []
for i in xrange(n):
result.append(a)
a, b = b, a + b
return result
for x in fibon(1000000):
print x,
db = MySQLdb.connect(host="localhost", user="root", passwd="root", db="domains")
cursor = db.cursor()
cursor.execute("SELECT domain FROM domains")
for result in ResultGenerator(cursor):
doSomethingWith(result)
db.close()
您可以更轻松地编写函数,如下所示:
# generator version
def fibon(n):
a = b = 1
for i in xrange(n):
yield a
a, b = b, a + b
功能更清晰。如果您使用这样的函数:
# function version
def fibon(n):
a = b = 1
result = []
for i in xrange(n):
result.append(a)
a, b = b, a + b
return result
for x in fibon(1000000):
print x,
db = MySQLdb.connect(host="localhost", user="root", passwd="root", db="domains")
cursor = db.cursor()
cursor.execute("SELECT domain FROM domains")
for result in ResultGenerator(cursor):
doSomethingWith(result)
db.close()
在本例中,如果使用生成器版本,则根本不会创建整个1000000项列表,一次只创建一个值。使用列表版本时,情况并非如此,列表将首先创建。我最喜欢使用的是“筛选”和“减少”操作
假设我们正在读取一个文件,只需要以“##”开头的行
然后我们可以在适当的循环中使用生成器函数
source= file( ... )
for line in filter2sharps( source.readlines() ):
print line
source.close()
reduce示例与此类似。假设我们有一个需要定位…
行块的文件。[不是HTML标记,而是恰好看起来像标记的行。]
def reduceLocation( aSequence ):
keep= False
block= None
for line in aSequence:
if line.startswith("</Location"):
block.append( line )
yield block
block= None
keep= False
elif line.startsWith("<Location"):
block= [ line ]
keep= True
elif keep:
block.append( line )
else:
pass
if block is not None:
yield block # A partial block, icky
其思想是,生成器函数允许我们过滤或减少一个序列,一次生成一个值的另一个序列。缓冲。如果在大数据块中提取数据是有效的,但在小数据块中处理数据,那么生成器可能会有帮助:
def bufferedFetch():
while True:
buffer = getBigChunkOfData()
# insert some code to break on 'end of data'
for i in buffer:
yield i
通过上述功能,您可以轻松地将缓冲与处理分开。消费者函数现在可以一个接一个地获取值,而不用担心缓冲。一堆堆东西。任何时候,只要您想生成一系列项目,但又不想同时将它们“具体化”到一个列表中。例如,您可以使用一个简单的生成器返回素数:
def primes():
primes_found = set()
primes_found.add(2)
yield 2
for i in itertools.count(1):
candidate = i * 2 + 1
if not all(candidate % prime for prime in primes_found):
primes_found.add(candidate)
yield candidate
然后,您可以使用它生成后续素数的乘积:
def prime_products():
primeiter = primes()
prev = primeiter.next()
for prime in primeiter:
yield prime * prev
prev = prime
这些都是相当简单的示例,但您可以看到它在处理大型(可能是无限的!)数据集时如何有用,而无需事先生成它们,这只是更明显的用途之一。简单的解释:
考虑< <代码>语句< < /> > <代码>
for item in iterable:
do_stuff()
很多时候,iterable
中的所有项目从一开始就不需要在那里,但可以根据需要动态生成。这两种方法都可以更有效
- 空间(您不需要同时存储所有项目)和
- 时间(迭代可能在需要所有项目之前完成)
for command in user_input():
do_stuff_with(command)
您无法事先知道所有用户的命令,但如果您有一个生成器处理您的命令,您可以使用这样一个很好的循环:
def user_input():
while True:
wait_for_command()
cmd = get_command()
yield cmd
使用生成器,您还可以在无限序列上进行迭代,这在容器上进行迭代时当然是不可能的。当web服务器充当代理时,我使用生成器:
def generate_integers(N):
for i in xrange(N):
yield i
In [1]: gen = generate_integers(3)
In [2]: gen
<generator object at 0x8117f90>
In [3]: gen.next()
0
In [4]: gen.next()
1
In [5]: gen.next()
def test():
for i in xrange(5):
val = yield
print(val)
t = test()
# Proceed to 'yield' statement
next(t)
# Send value to yield
t.send(1)
t.send('2')
t.send([3])
def ResultGenerator(cursor, batchsize=1000):
while True:
results = cursor.fetchmany(batchsize)
if not results:
break
for result in results:
yield result
return - returns only once
yield - returns multiple times
db = MySQLdb.connect(host="localhost", user="root", passwd="root", db="domains")
cursor = db.cursor()
cursor.execute("SELECT domain FROM domains")
for result in ResultGenerator(cursor):
doSomethingWith(result)
db.close()
class Rect():
def __init__(self, x, y, width, height):
self.l_top = (x, y)
self.r_top = (x+width, y)
self.r_bot = (x+width, y+height)
self.l_bot = (x, y+height)
def __iter__(self):
yield self.l_top
yield self.r_top
yield self.r_bot
yield self.l_bot
myrect=Rect(50, 50, 100, 100)
for corner in myrect:
print(corner)
def genprime(n=10):
for num in range(3, n+1):
for factor in range(2, num):
if num%factor == 0:
break
else:
yield(num)
for prime_num in genprime(100):
print(prime_num)