我正在经历一个生成器,有什么Pythonic方法可以确定当前元素是否是生成器的第一个或最后一个元素,因为它们需要特别小心?
感谢
基本上生成标签,所以我有像
这样的项目<div class="first">1</div>
<div>...</div>
<div class="last">n</div>
所以我想把最后一项保留在循环中?
答案 0 :(得分:5)
这是一个类似枚举的生成器,它向前跳过一个;它为最后一个元素返回-1。
>>> 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
答案 1 :(得分:3)
我这样做的方式类似于其他一些答案 - 但我这样做,作为优先选择。也许它也适合你的喜好。
使用下面的函数,我可以编写如下代码:
values = [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
答案 2 :(得分:2)
首先,使用一个标志来判断你是否处理过任何一个。最后,在变量中保存下一个值,如果没有,那就是最后一个。
答案 3 :(得分:1)
嗯,至于第一个元素:
for n, item in enumerate(generator()):
if n == 0:
# item is first
# out of the loop now: item is last
答案 4 :(得分:1)
将其转换为序列,例如:
>>> 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
不会让你更接近找到最后一个元素。
答案 5 :(得分:1)
当然,它违反了所有生成器的优点,但如果你的iterable不大,你应该使用:
list(gener)[1:-1]
答案 6 :(得分: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-based index t[1]
=按位标志,如果元素是第一个元素,则存在FLAGMASK_FIRST,如果元素是最后一个元素,则存在FLAGMASK_LAST t[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
我确信有更好的方法可以构建这样的东西,但无论如何这都是我的贡献。