[英]Pythonic way of determining if the current element is the first or last element of a generator?
I am going through a generator, whats the Pythonic way of determining if the current element is the first or last element of a generator, given that they need special care? 我正在经历一个生成器,为什么他们需要特别小心,以Pythonic方式确定当前元素是否是生成器的第一个或最后一个元素?
thanks 谢谢
basically generating tags, so i have items like 基本上生成标签,所以我有像
<div class="first">1</div>
<div>...</div>
<div class="last">n</div>
so i would like to keep the last item in loop? 所以我想保持循环中的最后一项?
Here's an enumerate-like generator that skips ahead one; 这是一个类似枚举的生成器,可以向前跳过一个; it returns -1 for the last element.
它为最后一个元素返回-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
It can't tell whether the generator passed to it is "fresh" or not, but it still tells you when the end is nigh: 它无法判断传递给它的发电机是否“新鲜”,但它仍然告诉你何时结束:
>>> 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
Once an iterator is used up, it raises StopIteration
as usual. 一旦迭代器用完,就像往常一样引发
StopIteration
。
>>> annotate(used_iter).next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in annotate
StopIteration
The way that I do this is similar to some of the other answers here - but I do it this way, as a matter of preference. 我这样做的方式类似于其他一些答案 - 但我这样做,作为优先选择。 Maybe it will suit your preferences too.
也许它也适合你的喜好。
With my function below, I can write code like this: 使用下面的函数,我可以编写如下代码:
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
Here is the function definition: 这是函数定义:
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 the first, use a flag to tell whether or not you've processed any. 对于第一个,使用标志来判断您是否已处理任何。 For the last, hold the next value in a variable, and if there are no more then that's the last one.
最后,在变量中保存下一个值,如果没有,那么这是最后一个。
Well, as for the first element: 那么,至于第一个元素:
for n, item in enumerate(generator()):
if n == 0:
# item is first
# out of the loop now: item is last
Turn it into a sequence, example: 把它变成一个序列,例如:
>>> gen = (x for x in range(5))
>>> L = list(gen)
>>> L[0]
0
>>> L[-1]
4
>>>
If you need to do this during the loop: 如果您需要在循环期间执行此操作:
>>> 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
>>>
Clearly, this is not the solution, if you are the one who created the generator, and need it to stay that way (for memory savings), but if you don't care, this is more Pythonic per se than setting flags (which is implicit at best, sine it relies on the last element during iteration persisting), and enumerate
won't get you any closer to finding the last element. 显然,这不是解决方案,如果你是创建生成器的人,并且需要它保持这种方式(为了节省内存),但是如果你不在乎,这比设置标志更加Pythonic本身(最好是隐式的,正则它依赖于迭代持久化过程中的最后一个元素),并且
enumerate
不会让你更接近找到最后一个元素。
当然,它违反了所有生成器的优点,但如果你的iterable不大,你应该使用:
list(gener)[1:-1]
If you're concerned about potentially large collections built dynamically, so that you don't want to temporarily place it into a single data structure, here's a different way: 如果您担心动态构建的潜在大型集合,那么您不希望将其暂时放入单个数据结构中,这是一种不同的方式:
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)
The function will produce a sequence of tuples, one for each element from the original collection. 该函数将生成一系列元组,每个元素对应一个原始集合。
The contents of the tuple: 元组的内容:
t[0]
= 0-based index t[0]
=基于0的索引 t[1]
= bitwise flags, FLAGMASK_FIRST is present if the element is the first element, FLAGMASK_LAST is present if the element is the last element t[1]
=按位标志,如果元素是第一个元素,则存在FLAGMASK_FIRST,如果元素是最后一个元素,则存在FLAGMASK_LAST t[2]
= The original element from the original collection t[2]
=原始集合中的原始元素 Sample output from the code above: 上面代码的示例输出:
+-- 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
I'm sure there are nicer ways to build this kinda thing, but this is my contribution anyway. 我确信有更好的方法可以构建这样的东西,但无论如何这是我的贡献。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.