Это, вероятно, лучший пример:
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
Те же значения генерируются, но в «обратном» версии, тики все приходят, а затем все КТВ. В версии «выход» тики и токи перемежаются.
Но главное различие между этими двумя способами проиллюстрированы следующими строками:
print ret # prints: [0, 2, 4, 6, 8]
print yld # prints: <generator object DoYield at 0x0000000002202630>
Здесь ret
список всех значений сгенерированных. То есть, когда это назначение сделано:
ret = generatorSample.DoReturn()
Весь список генерируется затем и вернулся в полном списке.
С генераторным подходом весь список не сгенерированный, по сути, ни один элемент не вычисляется. Просто ссылка на генератор, который будет производить элементы «только муха», по мере необходимости.
Другими словами, «возврат» подход:
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
был, скажем, 1 миллионом, а вычисление было более сложным, чем 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
Вы строите список внутри генератора, но вы (вероятно) * просто * хочу уступить значения. Вместо этого вы уступаете списку по мере его создания, что приводит к различию. Например, в 'DoYield()' вам вообще не нужны 'listResults', вы должны просто 'yield counter * 2' напрямую. –
@TomDalton Спасибо. Я думаю, что я понимаю и что вы имеете в виду. Тем не менее, я ожидал, что запуск DoYield() 'приведет к' tic [0] '' tic [0,2] '' tic [0,2,4] 'и т. Д., А затем' toc [0] 'toc [0,2]' и т. д. Я не понимаю, почему 'toc' печатается между операторами' tic'. – user131983
@ user131983 рассмотрите возможность запуска кода в нижней части моего ответа, посмотрите, помогает ли это объяснять, почему 'toc' печатается между операторами' tic' – jedwards