Pythonic确定当前元素是否是生成器的第一个或最后一个元素的方法?

时间:2011-05-12 19:18:30

标签: python

我正在经历一个生成器,有什么Pythonic方法可以确定当前元素是否是生成器的第一个或最后一个元素,因为它们需要特别小心?

感谢

基本上生成标签,所以我有像

这样的项目
<div class="first">1</div>
<div>...</div>
<div class="last">n</div>

所以我想把最后一项保留在循环中?

7 个答案:

答案 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

我确信有更好的方法可以构建这样的东西,但无论如何这都是我的贡献。