迭代访问列表的最“ pythonic”方法是什么?

我有一个Python脚本,它将一个整数列表做为输入,我须要一次处理四个整数。 不幸的是,我没法控制输入,或者将其做为四元素元组的列表传递。 目前,我正在以这种方式对其进行迭代: python

for i in xrange(0, len(ints), 4):
    # dummy op for example code
    foo += ints[i] * ints[i + 1] + ints[i + 2] * ints[i + 3]

不过,它看起来很像“ C思惟”,这使我怀疑还有一种处理这种状况的更Python的方法。 该列表在迭代后被丢弃,所以不须要保留。 也许这样的事情会更好? app

while ints:
    foo += ints[0] * ints[1] + ints[2] * ints[3]
    ints[0:4] = []

不过,仍是不太“正确”。 :-/ 测试

相关问题: 如何在Python中将列表分红均匀大小的块? spa


#1楼

此问题的理想解决方案适用于迭代器(而不单单是序列)。 它也应该很快。 code

这是itertools文档提供的解决方案: 对象

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

在Mac book air上使用ipython的%timeit ,每一个循环可得到47.5美圆。 ip

可是,这对我来讲真的不起做用,由于结果被填充为甚至大小的组。 没有填充的解决方案稍微复杂一些。 最幼稚的解决方案多是: 内存

def grouper(size, iterable):
    i = iter(iterable)
    while True:
        out = []
        try:
            for _ in range(size):
                out.append(i.next())
        except StopIteration:
            yield out
            break

        yield out

简单但很慢:每一个循环693 us 文档

我能想到的最佳解决方案是将islice用于内部循环: get

def grouper(size, iterable):
    it = iter(iterable)
    while True:
        group = tuple(itertools.islice(it, None, size))
        if not group:
            break
        yield group

使用相同的数据集,每一个循环可得到305 us。

没法以比这更快的速度得到纯解决方案,我为如下解决方案提供了一个重要的警告:若是输入数据中包含filldata实例,则可能会获得错误的答案。

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    for i in itertools.izip_longest(fillvalue=fillvalue, *args):
        if tuple(i)[-1] == fillvalue:
            yield tuple(v for v in i if v != fillvalue)
        else:
            yield i

我真的不喜欢这个答案,可是速度更快。 每一个循环124 us


#2楼

另外一个答案,其优势是:

1)容易理解
2)适用于任何可迭代的对象,而不单单是序列(上面的某些答案会在文件句柄上阻塞)
3)不会一次将块所有加载到内存中
4)不在内存中对同一迭代器创建一个大块的引用列表
5)列表末尾不填充填充值

话虽如此,我尚未计时,因此它可能比一些更聪明的方法要慢,而且某些优势可能与用例无关。

def chunkiter(iterable, size):
  def inneriter(first, iterator, size):
    yield first
    for _ in xrange(size - 1): 
      yield iterator.next()
  it = iter(iterable)
  while True:
    yield inneriter(it.next(), it, size)

In [2]: i = chunkiter('abcdefgh', 3)
In [3]: for ii in i:                                                
          for c in ii:
            print c,
          print ''
        ...:     
        a b c 
        d e f 
        g h

更新:
因为内循环和外循环从同一个迭代器中提取值,所以带来了一些缺点:
1)Continue在外部循环中没法按预期工做-它只是继续到下一个项目,而不是跳过一个块。 可是,这彷佛不是问题,由于在外循环中没有要测试的东西。
2)break在内部循环中没法正常工做-控件将在迭代器中的下一个项目中再次进入内部循环。 要跳过整个块,能够将内部迭代器(上面的ii)包装在一个元组中,例如for c in tuple(ii)中将for c in tuple(ii)打包,或者设置一个标志并耗尽迭代器。


#3楼

与其余建议相似,但不彻底相同,我喜欢这样作,由于它简单易读:

it = iter([1, 2, 3, 4, 5, 6, 7, 8, 9])
for chunk in zip(it, it, it, it):
    print chunk

>>> (1, 2, 3, 4)
>>> (5, 6, 7, 8)

这样,您将不会获得最后的部分块。 若是要获取(9, None, None, None)做为最后一块,只需使用itertools izip_longest


#4楼

使用小功能和事情确实对我没有吸引力。 我更喜欢只使用切片:

data = [...]
chunk_size = 10000 # or whatever
chunks = [data[i:i+chunk_size] for i in xrange(0,len(data),chunk_size)]
for chunk in chunks:
    ...

#5楼

我须要一个能够与集合和生成器一块儿使用的解决方案。 我没法提出任何简短而又漂亮的内容,但至少能够理解。

def chunker(seq, size):
    res = []
    for el in seq:
        res.append(el)
        if len(res) == size:
            yield res
            res = []
    if res:
        yield res

清单:

>>> list(chunker([i for i in range(10)], 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

组:

>>> list(chunker(set([i for i in range(10)]), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

发电机:

>>> list(chunker((i for i in range(10)), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
相关文章
相关标签/搜索