bod*_*ydo 68 python generator peek
我无法弄清楚如何在Python生成器中展望一个元素.一看,它就消失了.
这就是我的意思:
gen = iter([1,2,3])
next_value = gen.next() # okay, I looked forward and see that next_value = 1
# but now:
list(gen) # is [2, 3] -- the first value is gone!
Run Code Online (Sandbox Code Playgroud)
这是一个更实际的例子:
gen = element_generator()
if gen.next_value() == 'STOP':
quit_application()
else:
process(gen.next())
Run Code Online (Sandbox Code Playgroud)
任何人都可以帮我写一个发电机,你可以看一个元素前进吗?
Dav*_*d Z 69
为了完整起见,more-itertools
包(应该是任何Python程序员工具箱的一部分)包括一个peekable
实现此行为的包装器.正如文档中的代码示例所示:
>>> p = peekable(xrange(2))
>>> p.peek()
0
>>> p.next()
0
>>> p.peek()
1
>>> p.next()
1
Run Code Online (Sandbox Code Playgroud)
该软件包兼容Python 2和3,即使文档显示了Python 2语法.
Aar*_*lla 55
Python生成器API是一种方式:你不能推回你读过的元素.但是您可以使用itertools模块创建一个新的迭代器并添加元素:
import itertools
gen = iter([1,2,3])
peek = gen.next()
print list(itertools.chain([peek], gen))
Run Code Online (Sandbox Code Playgroud)
plo*_*lof 25
好的 - 两年太晚了 - 但我遇到了这个问题,并没有找到任何满意的答案.想出了这个元生成器:
class Peekorator(object):
def __init__(self, generator):
self.empty = False
self.peek = None
self.generator = generator
try:
self.peek = self.generator.next()
except StopIteration:
self.empty = True
def __iter__(self):
return self
def next(self):
"""
Return the self.peek element, or raise StopIteration
if empty
"""
if self.empty:
raise StopIteration()
to_return = self.peek
try:
self.peek = self.generator.next()
except StopIteration:
self.peek = None
self.empty = True
return to_return
def simple_iterator():
for x in range(10):
yield x*3
pkr = Peekorator(simple_iterator())
for i in pkr:
print i, pkr.peek, pkr.empty
Run Code Online (Sandbox Code Playgroud)
结果是:
0 3 False
3 6 False
6 9 False
9 12 False
...
24 27 False
27 None False
Run Code Online (Sandbox Code Playgroud)
即,您在迭代期间的任何时刻都可以访问列表中的下一个项目.
Jon*_*ley 16
您可以使用itertools.tee生成生成器的轻量级副本.然后在一份副本上偷看不会影响第二份副本:
import itertools
def process(seq):
peeker, items = itertools.tee(seq)
# initial peek ahead
# so that peeker is one ahead of items
if next(peeker) == 'STOP':
return
for item in items:
# peek ahead
if next(peeker) == "STOP":
return
# process items
print(item)
Run Code Online (Sandbox Code Playgroud)
'items'生成器不受你骚扰'peeker'的影响.请注意,在调用'tee'之后你不应该使用原来的'seq',否则会破坏它.
FWIW,这是解决这个问题的错误方法.任何要求您在生成器中查找前面的项目的算法也可以编写为使用当前生成器项目和前一项目.那么你不必破坏你对发生器的使用,你的代码会简单得多.请参阅我对此问题的其他答案.
一个简单的解决方案是使用这样的函数:
def peek(it):
first = next(it)
return first, itertools.chain([first], it)
Run Code Online (Sandbox Code Playgroud)
然后你可以这样做:
>>> it = iter(range(10))
>>> x, it = peek(it)
>>> x
0
>>> next(it)
0
>>> next(it)
1
Run Code Online (Sandbox Code Playgroud)
>>> gen = iter(range(10))
>>> peek = next(gen)
>>> peek
0
>>> gen = (value for g in ([peek], gen) for value in g)
>>> list(gen)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Run Code Online (Sandbox Code Playgroud)
只是为了好玩,我根据亚伦的建议创建了一个超前类的实现:
import itertools
class lookahead_chain(object):
def __init__(self, it):
self._it = iter(it)
def __iter__(self):
return self
def next(self):
return next(self._it)
def peek(self, default=None, _chain=itertools.chain):
it = self._it
try:
v = self._it.next()
self._it = _chain((v,), it)
return v
except StopIteration:
return default
lookahead = lookahead_chain
Run Code Online (Sandbox Code Playgroud)
这样,以下将起作用:
>>> t = lookahead(xrange(8))
>>> list(itertools.islice(t, 3))
[0, 1, 2]
>>> t.peek()
3
>>> list(itertools.islice(t, 3))
[3, 4, 5]
Run Code Online (Sandbox Code Playgroud)
通过这种实现,连续多次调用偷看是一个坏主意。
在查看CPython源代码时,我发现了一个更短,更有效的更好的方法:
class lookahead_tee(object):
def __init__(self, it):
self._it, = itertools.tee(it, 1)
def __iter__(self):
return self._it
def peek(self, default=None):
try:
return self._it.__copy__().next()
except StopIteration:
return default
lookahead = lookahead_tee
Run Code Online (Sandbox Code Playgroud)
用法与上面相同,但您无需为此付出任何代价就可以连续使用peek多次。再增加几行,您还可以在迭代器中查找多个项(最多可用RAM)。
一个迭代器,允许查看下一个元素以及更远的元素。它根据需要提前读取并记住 a 中的值deque
。
from collections import deque
class PeekIterator:
def __init__(self, iterable):
self.iterator = iter(iterable)
self.peeked = deque()
def __iter__(self):
return self
def __next__(self):
if self.peeked:
return self.peeked.popleft()
return next(self.iterator)
def peek(self, ahead=0):
while len(self.peeked) <= ahead:
self.peeked.append(next(self.iterator))
return self.peeked[ahead]
Run Code Online (Sandbox Code Playgroud)
演示:
>>> it = PeekIterator(range(10))
>>> it.peek()
0
>>> it.peek(5)
5
>>> it.peek(13)
Traceback (most recent call last):
File "<pyshell#68>", line 1, in <module>
it.peek(13)
File "[...]", line 15, in peek
self.peeked.append(next(self.iterator))
StopIteration
>>> it.peek(2)
2
>>> next(it)
0
>>> it.peek(2)
3
>>> list(it)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
28468 次 |
最近记录: |