Python中生成器和函数的区别

时间:2014-10-01 23:14:37

标签: python

我试图理解函数和生成器之间的区别,并为此目的使用了下面的代码。但是,我不理解输出。

class GeneratorsSample(object):

    def DoReturn(self):
        counter, maxCounter = 0, 5
        listResults = []
        while counter < maxCounter:
            print "tic"
            listResults.append(counter*2)
            counter += 1
        return listResults

    def DoYield(self):
        counter, maxCounter = 0, 5
        listResults = []
        while counter < maxCounter:
            print "tic"
            listResults.append(counter*2)
            yield listResults  #to return the entire list
            #yield listResults[counter] #you can only return one item at a time
            counter += 1
        return


generatorSample = GeneratorsSample()

我不明白为什么,DoReturn()的输出与DoYield()的输出不同。例如,

returnResults = generatorSample.DoReturn()
for r in returnResults:
    print "toc", r

输出:

tic
tic
tic
tic
tic
toc 0
toc 2
toc 4
toc 6
toc 8

而且,

yieldResults = generatorSample.DoYield()   
for r in yieldResults:      
    print "toc", r

输出:

tic
toc [0]
tic
toc [0, 2]
tic
toc [0, 2, 4]
tic
toc [0, 2, 4, 6]
tic
toc [0, 2, 4, 6, 8]

1 个答案:

答案 0 :(得分:2)

这可能是一个更好的例子:

class GeneratorsSample(object):
    def DoReturn(self):
        counter, maxCounter = 0, 5
        listResults = []
        while counter < maxCounter:
            print "tic"
            listResults.append(counter*2)
            counter += 1
        return listResults

    def DoYield(self):
        counter, maxCounter = 0, 5
        while counter < maxCounter:
            print "tic"
            yield counter*2
            counter += 1
        return


generatorSample = GeneratorsSample()

ret = generatorSample.DoReturn()
yld = generatorSample.DoYield()

for r in ret: print "toc", r
for r in yld: print "toc", r

print ret
print yld

首先看看以下几行:

for r in ret: print "toc", r
for r in yld: print "toc", r

生成相同的值,但在&#34; return&#34;版本,抽搐都是第一个,然后是所有的tocs。在&#34;产量&#34;版本tics和tocs是穿插的。

但这两种方法之间的关键区别如下:

print ret  # prints: [0, 2, 4, 6, 8]
print yld  # prints: <generator object DoYield at 0x0000000002202630>

此处,ret是生成的所有值的列表。也就是说,在进行此分配时:

ret = generatorSample.DoReturn()

整个列表生成然后,并作为完整列表返回。

使用生成器方法,生成整个列表 ,实际上,没有计算任何元素。只是对生成器的引用,它将生成元素&#34;只需要飞行&#34;,根据需要。

换句话说,&#34;返回&#34;的方法:

generates a number
generates a number
generates a number
...
uses that number
uses that number
uses that number
...

而发电机方法:

generates a number
uses that number
generates a number
uses that number
...

生成器的效率在于它们只需要花时间生成单个元素,因为它们是需要的(如果它们是需要的话)。如果maxCounter是100万,并且计算比counter * 2更复杂,那么您获得第一个输出所需的时间会有明显的改善。

事实上,您可以通过添加人工延迟(此处为time.sleep(1)

来查看)
import time

class GeneratorsSample(object):
    def DoReturn(self):
        counter, maxCounter = 0, 5
        listResults = []
        while counter < maxCounter:
            v = counter * 2
            time.sleep(1)
            print "[DoReturn] computed %d" % v
            listResults.append(v)
            counter += 1
        return listResults

    def DoYield(self):
        counter, maxCounter = 0, 5
        while counter < maxCounter:
            v = counter * 2
            time.sleep(1)
            print "[DoYield]  computed %d" % v
            yield counter*2
            counter += 1

        return


generatorSample = GeneratorsSample()

ret = generatorSample.DoReturn()
yld = generatorSample.DoYield()

for r in ret: print "[return loop] using", r
print("")
for r in yld: print "[yield loop]  using", r

输出为:

[DoReturn] computed 0
[DoReturn] computed 2
[DoReturn] computed 4
[DoReturn] computed 6
[DoReturn] computed 8
[return loop] using 0
[return loop] using 2
[return loop] using 4
[return loop] using 6
[return loop] using 8

[DoYield]  computed 0
[yield loop]  using 0
[DoYield]  computed 2
[yield loop]  using 2
[DoYield]  computed 4
[yield loop]  using 4
[DoYield]  computed 6
[yield loop]  using 6
[DoYield]  computed 8
[yield loop]  using 8