我正在使用一个生成器,有没有Pythonic的方法来确定当前元素是否是生成器的第一个或最后一个元素,因为它们需要特殊处理?
谢谢。
基本上是生成标签,所以我有像这样的项目
<div class="first">1</div>
<div>...</div>
<div class="last">n</div>
所以我想在循环中保留最后一个项目?
我正在使用一个生成器,有没有Pythonic的方法来确定当前元素是否是生成器的第一个或最后一个元素,因为它们需要特殊处理?
谢谢。
基本上是生成标签,所以我有像这样的项目
<div class="first">1</div>
<div>...</div>
<div class="last">n</div>
>>> def annotate(gen):
... prev_i, prev_val = 0, gen.next()
... for i, val in enumerate(gen, start=1):
... yield prev_i, prev_val
... prev_i, prev_val = i, val
... yield '-1', prev_val
>>> for i, val in annotate(iter(range(4))):
... print i, val
...
0 0
1 1
2 2
-1 3
>>> used_iter = iter(range(5))
>>> used_iter.next()
0
>>> for i, val in annotate(used_iter):
... print i, val
...
0 1
1 2
2 3
-1 4
一旦迭代器被使用完,它会像平常一样引发 StopIteration
异常。
>>> annotate(used_iter).next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in annotate
StopIteration
next()
方法的对象会报错。为了避免这种情况,你可以在 annotate
函数的开头添加 gen = iter(gen)
。 - senderlevalues = [10, 11, 12, 13]
for i, val, isfirst, islast in enumerate2(values):
if isfirst:
print 'BEGIN...', val
elif islast:
print val, '... END'
else:
print val
以下是函数定义:
def enumerate2(iterable_):
it = iter(iterable_)
try:
e = it.next()
isfirst = True
i = 0
try:
while True:
next_e = it.next()
yield (i, e, isfirst, False)
i += 1
isfirst = False
e = next_e
except StopIteration:
yield (i, e, isfirst, True)
except StopIteration:
pass
首先,使用一个标志来告诉你是否已经处理了任何内容。最后,将下一个值保存在变量中,如果没有更多值,则该值为最后一个。
好的,关于第一个元素:
for n, item in enumerate(generator()):
if n == 0:
# item is first
# out of the loop now: item is last
将其转换为序列,例如:
>>> gen = (x for x in range(5))
>>> L = list(gen)
>>> L[0]
0
>>> L[-1]
4
>>>
如果你需要在循环期间执行此操作:
>>> gen = (x for x in range(5))
>>> L = list(gen)
>>> for idx, item in enumerate(L):
... if idx == 0:
... print(u'{item} is first'.format(item=item))
... if idx == len(L) - 1:
... print(u'{item} is last'.format(item=item))
...
0 is first
4 is last
>>>
显然,这不是解决方案,如果你是创建生成器的人,并且需要它保持原样(为了节省内存),但如果你不在意,这比设置标志更符合Pythonic本质(最好是隐式的,因为它依赖于迭代期间最后一个元素的持久性),而enumerate
不会让你更接近找到最后一个元素。
当然,这违反了所有生成器的优点,但如果您的可迭代对象不是很大,您应该使用:
list(gener)[1:-1]
如果您担心可能会构建动态大型集合,因此不想将其暂时放入单个数据结构中,则可以尝试另一种方式:
FLAGMASK_FIRST = 1
FLAGMASK_LAST = 2
def flag_lastfirst(collection):
first_flag = FLAGMASK_FIRST
first = True
index = 0
for element in collection:
if not first:
yield index, first_flag, current
index += 1
first_flag = 0
current = element
first = False
if not first:
yield index, first_flag | FLAGMASK_LAST, current
l = [1, 2, 3, 4]
for k in flag_lastfirst(l):
print(k)
该函数将为原始集合中的每个元素生成一个元组序列。
元组的内容:
t[0]
= 基于0的索引t[1]
= 位标志,如果元素是第一个元素,则存在FLAGMASK_FIRST,如果元素是最后一个元素,则存在FLAGMASK_LASTt[2]
= 原始集合中的原始元素上述代码的示例输出:
+-- 0-based index
v
(0, 1, 1)
(1, 0, 2)
(2, 0, 3)
(3, 2, 4)
^ ^
| +-- the element from the original collection
|
+-- 1 means first, 2 means last,
3 means both first and last, 0 is everything else
我相信有更好的方法来构建这种东西,但这是我的贡献。