我有一個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
此問題的理想解決方案適用於迭代器(而不單單是序列)。 它也應該很快。 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
另外一個答案,其優勢是:
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)
打包,或者設置一個標誌並耗盡迭代器。
與其餘建議相似,但不徹底相同,我喜歡這樣作,由於它簡單易讀:
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
。
使用小功能和事情確實對我沒有吸引力。 我更喜歡只使用切片:
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: ...
我須要一個能夠與集合和生成器一塊兒使用的解決方案。 我沒法提出任何簡短而又漂亮的內容,但至少能夠理解。
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]]