[英]How can I limit iterations of a loop in Python?
假設我有一個項目列表,我想迭代其中的前幾個:
items = list(range(10)) # I mean this to represent any kind of iterable.
limit = 5
來自其他語言的 Python naïf 可能會編寫這個非常有用和高性能(如果單語)的代碼:
index = 0
for item in items: # Python's `for` loop is a for-each.
print(item) # or whatever function of that item.
index += 1
if index == limit:
break
但是 Python 有 enumerate,它很好地包含了大約一半的代碼:
for index, item in enumerate(items):
print(item)
if index == limit: # There's gotta be a better way.
break
所以我們將額外的代碼減半。 但一定有更好的方法。
如果 enumerate 采用另一個可選的stop
參數(例如,它采用這樣的start
參數: enumerate(items, start=1)
),我認為這是理想的,但以下不存在(請參閱enumerate 上的文檔在這里):
# hypothetical code, not implemented:
for _, item in enumerate(items, start=0, stop=limit): # `stop` not implemented
print(item)
請注意,不需要命名index
因為不需要引用它。
有沒有一種慣用的方式來寫上面的內容? 怎么樣?
第二個問題:為什么這不內置在枚舉中?
如何限制 Python 中循環的迭代?
for index, item in enumerate(items): print(item) if index == limit: break
有沒有更短的、慣用的方式來寫上面的內容? 怎么樣?
zip
在其參數中最短的可迭代對象上停止。 (與zip_longest
的行為zip_longest
,后者使用最長的可迭代對象。)
range
可以提供一個有限的迭代器,我們可以將它與我們的主要迭代器一起傳遞給 zip。
所以我們可以將一個range
對象(帶有它的stop
參數)傳遞給zip
並像有限枚舉一樣使用它。
zip(range(limit), items)
使用 Python 3, zip
和range
返回可迭代對象,它們對數據進行管道傳輸,而不是在中間步驟的列表中具體化數據。
for index, item in zip(range(limit), items):
print(index, item)
要在 Python 2 中獲得相同的行為,只需將xrange
替換為range
並將itertools.izip
替換為zip
。
from itertools import izip
for index, item in izip(xrange(limit), items):
print(item)
itertools.islice
您可以使用itertools.islice
:
for item in itertools.islice(items, 0, stop):
print(item)
這不需要分配給索引。
enumerate(islice(items, stop))
以獲取索引正如 Pablo Ruiz Ruiz 所指出的,我們也可以用 enumerate 組合 islice。
for index, item in enumerate(islice(items, limit)):
print(index, item)
為什么這不內置在
enumerate
?
這是在純 Python 中實現的枚舉(可能進行修改以在注釋中獲得所需的行為):
def enumerate(collection, start=0): # could add stop=None
i = start
it = iter(collection)
while 1: # could modify to `while i != stop:`
yield (i, next(it))
i += 1
對於那些已經使用 enumerate 的人來說,上面的性能會降低,因為它必須檢查是否是時候停止每次迭代了。 如果沒有停止參數,我們可以檢查並使用舊的枚舉:
_enumerate = enumerate
def enumerate(collection, start=0, stop=None):
if stop is not None:
return zip(range(start, stop), collection)
return _enumerate(collection, start)
這種額外的檢查對性能的影響可以忽略不計。
至於為什么enumerate 沒有停止參數,這是最初提出的(參見PEP 279 ):
這個函數最初是用可選的開始和停止參數提出的。 GvR [Guido van Rossum] 指出函數調用
enumerate(seqn, 4, 6)
有一個替代的、合理的解釋,作為一個切片,將返回序列的第四和第五個元素。 為了避免歧義,可選參數被刪除,即使這意味着失去作為循環計數器的靈活性。 這種靈活性對於從一開始計數的常見情況最為重要,例如:for linenum, line in enumerate(source,1): print linenum, line
所以顯然start
被保留是因為它非常有價值,而stop
被刪除是因為它的用例較少並且導致了對新函數使用的混淆。
另一個答案說:
為什么不簡單地使用
for item in items[:limit]: # or limit+1, depends
這里有一些缺點:
當您了解限制以及它是制作副本還是視圖時,您應該只使用帶有下標符號的切片。
我認為現在 Python 社區知道 enumerate 的用法,混淆成本將被參數的價值所抵消。
在此之前,您可以使用:
for index, element in zip(range(limit), items):
...
或
for index, item in enumerate(islice(items, limit)):
...
或者,如果您根本不需要索引:
for element in islice(items, 0, limit):
...
並避免使用下標符號進行切片,除非您了解這些限制。
您可以為此使用itertools.islice
。 它接受start
、 stop
和step
參數,如果您只傳遞一個參數,則將其視為stop
。 它適用於任何可迭代對象。
itertools.islice(iterable, stop)
itertools.islice(iterable, start, stop[, step])
演示:
>>> from itertools import islice
>>> items = list(range(10))
>>> limit = 5
>>> for item in islice(items, limit):
print item,
...
0 1 2 3 4
來自文檔的示例:
islice('ABCDEFG', 2) --> A B
islice('ABCDEFG', 2, 4) --> C D
islice('ABCDEFG', 2, None) --> C D E F G
islice('ABCDEFG', 0, None, 2) --> A C E G
為什么不簡單地使用
for item in items[:limit]: # or limit+1, depends
print(item) # or whatever function of that item.
這僅適用於某些可迭代對象,但由於您指定了列表,因此它有效。
如果您使用 Sets 或 dicts 等,則它不起作用。
使用枚舉內的限制通過 islice
a = [2,3,4,2,1,4]
for a, v in enumerate(islice(a, 3)):
print(a, v)
輸出:
0 2
1 3
2 4
為什么不循環直到限制或列表末尾,以較早發生者為准,如下所示:
items = range(10)
limit = 5
for i in range(min(limit, len(items))):
print items[i]
輸出:
0
1
2
3
4
簡短的解決方案
items = range(10)
limit = 5
for i in items[:limit]: print(i)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.