>> |
No.8152
Файл: 1272217430528.jpg -(36 KB, 400x400, 1272217430528.jpg)
Анонсированный ранее минигайд в читаемом виде: http://paste.lisp.org/+2VEB
Чтобы не потерялось:
Для начала небольшое напоминание о том что есть объект-итератор (честно спизжено отсюда - http://stackoverflow.com/questions/19151/build-a-basic-python-iterator )
>>> class Counter:
... def __init__(self,low,high):
... self.current = low
... self.high = high
... def __iter__(self):
... return self
... def next(self):
... if self.current >self.high:
... raise StopIteration
... else:
... self.current +=1
... return self.current - 1
...
>>> [c for c in Counter(3,8)]
[3, 4, 5, 6, 7, 8]
>>> [c for c in Counter(3,8)]
[3, 4, 5, 6, 7, 8]
>>> i = Counter(3,8)
>>> [c for c in i]
[3, 4, 5, 6, 7, 8]
>>> [c for c in i]
[]
>>> (c for c in Counter(3,8)) # хитрый синтаксис для создания генераторов
<generator object <genexpr> at 0x7fac32f85460>
>>> [x for x in (c for c in Counter(3,8))] # а теперь обратно в список
[3, 4, 5, 6, 7, 8]
По сути итератор - это объект с методом next(), который возвращает очередное значение и бросает StopIteration когда возвращать больше нечего. Такой объект можно дергать вручную, можно передать в for ( как обычный так и списковый из примера выше )
Теперь о генераторах. По сути это тоже объекты-итераторы, просто для их создания ввели свой синтаксический сахар. Вот аналог итератора из примера выше:
>>> def getCounter(low,high):
... current = low
... while current <= high:
... yield current
... current = current + 1
...
>>> getCounter(3,8)
<generator object getCounter at 0x7fac32f854b0>
>>> [x for x in getCounter(3,8)]
[3, 4, 5, 6, 7, 8]
>>> getCounter(3,8).next()
3
Как видим, мы создали функцию, которая возвращает объект-генератор, который является итератором(удовлетворяет интерфейсу?) и может быть дернут как через for так и вручную.
Самое интересное - то что происходит при вызове yield. Последовательное выполнение генератора прерывается и поток управления переходит к месту вызова метода next() генератора, возвращая значение, которое передано в yield-оператор.
>>> def genT():
... print "in generator!"
... yield 10
... print "second one"
... yield 20
...
>>> g = genT()
>>> g .next()
in generator!
10
>>> g .next()
second one
20
>>> g .next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> g .next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Также на базе генераторов можно делать сопрограммы, передавая при итерациях значения внутрь генератора. Такой подход исопльзуется в некоторых библиотеках и позволяет очень сильно сократить код, убрав при этом значительную часть макаронности.
>>> def genGuessNum(n):
... str = "init guesser"
... while True:
... t = yield str
... if n==t:
... yield "bingo!"
... break
... else: str = "not this number,try again!"
...
>>> g = genGuessNum(3)
>>> g.next()
'init guesser'
>>> g.send(1)
'not this number,try again!'
>>> g.send(2)
'not this number,try again!'
>>> g.send(10)
'not this number,try again!'
>>> g.send(3)
'bingo!'
>>> g.send(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> g.send(100)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> g.next(100)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: expected 0 arguments, got 1
|