简体   繁体   English

用于计数器的python循环?

[英]for loop in python with counter?

How can I create a for loop with a counter? 如何使用计数器创建for循环? I have a list, and I want to read an element after each n elements. 我有一个列表,我想在每个n个元素之后读取一个元素。 I'd initially done this 我最初这样做了

for i in enumerate(n):
    print(i)

But as expected it prints every element instead of every nth element, what would be the Python way of solving this? 但正如预期的那样,它会打印每个元素而不是每个第n个元素,Python的解决方法是什么?

Use the itertools.islice() object to limit iteration to every n -th object, this is at least twice as fast as any other proposed solution: 使用itertools.islice()对象将迭代限制为每个第n个对象,这至少是任何其他提议的解决方案的两倍:

from itertools import islice

n = 5
for ob in islice(iterable, None, None, n):
    print(ob)

The above efficiently produces every 5th object, starting at the first: 第一个开始 ,上面有效地生成每第5个对象:

>>> from itertools import islice
>>> from string import ascii_uppercase as iterable
>>> n = 5
>>> for ob in islice(iterable, None, None, n):
...     print(ob)
...
A
F
K
P
U
Z

Replace the first None with n - 1 if you want to skip to the n th object as the first to use: 如果要跳转到第n个对象作为第一个使用,请将第一个None替换为n - 1

>>> for ob in islice(iterable, n - 1, None, n):
...     print(ob)
...
E
J
O
T
Y

No copy of the input sequence is created to achieve this, so no additional memory or time is needed to produce the results. 没有创建输入序列的副本来实现此目的,因此不需要额外的内存或时间来产生结果。 And taking every n -th object is done more efficiently than a % modulus test against an index from enumerate() could ever make it, or using range() to generate an index. 并且对于来自enumerate()的索引的%模数测试可以更有效地完成每个第n个对象,或者使用range()来生成索引。 That's because no further Python bytecode steps are needed to make those extra tests or index operations. 那是因为不需要进一步的Python字节码步骤来进行那些额外的测试或索引操作。

If you also needed to have the index of the items selected this way, add enumerate() back in by wrapping the iterable: 如果您需要以这种方式选择项目的索引,请通过包装iterable来重新添加enumerate()

>>> for i, ob in islice(enumerate(iterable), n - 1, None, n):
...     print(i, ob)
...
4 E
9 J
14 O
19 T
24 Y

islice() beats any other solution hands-down if you need speed: 如果你需要速度, islice()击败任何其他解决方案:

>>> from timeit import timeit
>>> from itertools import islice
>>> import random
>>> testdata = [random.randrange(1000) for _ in range(1000000)]  # 1 million random numbers
>>> def islice_loop(it):
...     for ob in islice(it, None, None, 5): pass
...
>>> def range_loop(it):
...     for i in range(0, len(it), 5): ob = it[i]
...
>>> def slice_loop(it):
...     for ob in it[::5]: pass
...
>>> def enumerate_test_loop(it):
...     for i, ob in enumerate(it):
...         if i % 5 == 0: pass
...
>>> def enumerate_list_slice_loop(it):
...     for i, ob in list(enumerate(it))[::5]: pass
...
>>> timeit('tf(t)', 'from __main__ import testdata as t, islice_loop as tf', number=1000)
4.194277995004086
>>> timeit('tf(t)', 'from __main__ import testdata as t, range_loop as tf', number=1000)
11.904250939987833
>>> timeit('tf(t)', 'from __main__ import testdata as t, slice_loop as tf', number=1000)
8.32347785399179
>>> timeit('tf(t)', 'from __main__ import testdata as t, enumerate_list_slice_loop as tf', number=1000)
198.1711291699903

So, for 1 million inputs, and 1000 tests, the enumerate() approach took sixteen times as much time as the islice() version, and the list(enumerate(...))[::n] copy-and-slice operation took almost 3 minutes to run the 1000 tests, clocking in at almost fifty times slower execution time . 因此,对于100万个输入和1000个测试, enumerate()方法花费的时间是islice()版本和list(enumerate(...))[::n]复制和切片的16倍。操作花了将近3分钟来完成1000次测试, 执行时间慢近五十倍 Don't ever use that option! 不要使用那个选项!

I am not sure wich kind of value is n but usually there are this ways: (for me, n is a list) 我不知道至极样的价值是n ,但通常有这样的方法:(对我来说, n是一个列表)

for index, item in enumerate(n):
   if index % nth == 0: # If the output is not like you want, try doing (index + 1), or enumerate(n, start=1).
       print(item)

Other way could be: 其他方式可能是:

for index in range(0, len(n), nth): # Only work with sequences
   print(n[index]) # If the output is not like you want, try doing n[index + 1]

Or: 要么:

for item in n[::nth]: # Low perfomance and hight memory consumption warning!! Only work with sequences
    print(item)

Even you can combine the first one with the last one: 即使你可以将第一个与最后一个结合起来:

for i, item in list(enumerate(n))[::nth]: # Huge low perfomance warning!!!
    print(item)

But I'm not sure if that has an advantage... 但我不确定这是否有优势......

Also, if you are willing to make a function, you could do something similar to the enumerate function: 此外,如果您愿意创建一个函数,您可以执行与enumerate函数类似的操作:

def myEnumerate(sequence, start=0, jump=1):
    n = start
    j = start // Or j = 0, that is your decision.
    for elem in sequence:
        if j % jump == 0:
            yield n, elem
            n += 1
        j += 1

for index, item in myEnumerate(n, jump=1):
    print(item)

Personally, I wouldn't do this last one. 就个人而言,我最后不会这样做。 I'm not sure why but it's a feeling. 我不确定为什么,但这是一种感觉。

Perfomance test 性能测试

n = 'a b c d e f g h i j k l m n ñ o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9 ! " · $ % & / ( ) = ? ¿ Ç ç } { [ ] ; : _ ¨ ^ * ` + ´ - . , º ª \ /'.split(" ")
nth = 3    
def a():
    for i, item in enumerate(n):
       if i % nth == 0:
           item       
def b():
    for item in range(0, len(n), nth):
       n[item]           
def c():
    for item in n[::nth]:
        item    
def d():
    for i, item in list(enumerate(n))[::nth]:
       if i % nth == 0:
           item    
def enumerates(sequence, start=0, jump=1):
    n = start
    j = start
    for elem in sequence:
        if j % jump == 0:
            yield n, elem
            n += 1
        j += 1            
def e():
    for i, item in enumerates(n, jump= nth):
        item    
if __name__ == '__main__':
    import timeit
    print(timeit.timeit("a()", setup="from __main__ import a")) # 10.556324407152305
    print(timeit.timeit("b()", setup="from __main__ import b")) # 2.7166204783010137
    print(timeit.timeit("c()", setup="from __main__ import c")) # 1.0285353306076601
    print(timeit.timeit("d()", setup="from __main__ import d")) # 8.283859051918608
    print(timeit.timeit("e()", setup="from __main__ import e")) # 14.91601851631981

But if you are really looking for perfomance you should read @Martijn Pieters answer . 但如果你真的在寻找性能,你应该阅读@Martijn Pieters的回答

Just use a range: 只需使用范围:

for i in range(0, size, n):
    print (i)

enumerate returns a sequence of tuples that look like (index, value) enumerate返回一系列看起来像(index, value)的元组

Say your list is my_list . 说你的清单是my_list You could do something like 你可以做点什么

for index, value in enumerate(my_list):
    if index % n == 0: #it's an nth item
        print(value)

This is the python way of adding an index to a for loop. 这是向for循环添加索引的python方法。

Some Alternatives 一些替代品

If your goal is to do something with the list elements by n's, here are some alternative solutions that don't necessarily add an index to your loop, but will get you where you need to be: 如果您的目标是通过n来对列表元素执行某些操作,那么以下是一些替代解决方案,它们不一定会为您的循环添加索引,但会让您获得所需的位置:

Array Slicing 阵列切片

You could also use an array slice with a step 您还可以使用带有步骤的数组切片

nth_elements = my_array[0::n]
for e in nth_elements:
    print(e)

The advantage is you're now working with a smaller list, at the cost of more memory and the time to make a copy, but that might be advantageous if you're doing several operations with it. 优点是你现在正在处理一个较小的列表,代价是更多的内存和制作副本的时间,但如果你用它做几个操作,这可能是有利的。 It's quick to write and easy to read. 它写得快,易于阅读。

Range 范围

You could so something similar with the range function 您可以使用范围功能进行类似的操作

for index in range(0, len(my_list), n):
    print(n)

Note: if you're using python2, use xrange is preferred. 注意:如果您使用的是python2,请首选使用xrange

This just gets you the index, not the index and value. 这只是获取索引,而不是索引和值。 It's fast and memory efficient. 它速度快,内存效率高。 range (or xrange ) is lazy and you're using an index directly into the array, so you don't have to touch each element. range (或xrange )是懒惰的,你直接在数组中使用索引,所以你不必触及每个元素。

Itertools Itertools

If you would like a lazy sequence, you could use itertools . 如果你想要一个懒惰的序列,你可以使用itertools This might be useful if having the subsequence is too large to fit in memory comfortably. 如果子序列太大而无法舒适地适应内存,这可能很有用。

from itertools import islice
for element in islice(my_list, 0, len(my_list), n):
    print(element)

This is fast and memory efficient, but it requires an import. 这样可以快速且节省内存,但需要导入。 Sure, it's from the standard library, but if you're writing code for a small device, maybe it's not available. 当然,它来自标准库,但如果您正在为小型设备编写代码,可能它不可用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM