迭代訪問列表的最「 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]]
相關文章
相關標籤/搜索