【问题标题】:Iterate over list starting from a certain index从某个索引开始迭代列表
【发布时间】:2019-05-25 23:22:24
【问题描述】:

这是一个例子:

list_ = [5, 'cat', 0xDEADBEEF, 4.0]

for offset in range(len(list_)):
    result = 0
    for elem in list_[offset:]:
        result = func(result, elem)
    return result

其中func 是不可交换的。

在上面的代码中,list_[offset:] 将创建一个新列表,但我只需要查看list_。我该如何优化它?

【问题讨论】:

  • 在大多数情况下,切片是最有效的。
  • @tdelaney,在这种情况下,不是这样。列表切片在 O(k) 时间内工作,其中 k 是切片的大小。但是 O(1) 对于 OP 的算法来说就足够了,例如通过deque.
  • @tdelaney,对我来说,这似乎是一个XY problem,这是我已经回答的。你有理由相信副本能更好地解决核心问题吗?
  • @nalzok,我希望return 应该与顶部for 对齐?

标签: python arrays list iterator iteration


【解决方案1】:

要复制您的切片,但每次迭代的时间为 O(1),您可以使用 collections.dequepopleft

from collections import deque

dq = deque(list_)

for i in range(len(dq)):
    print(dq)
    dq.popleft()

结果:

deque([5, 'cat', 3735928559, 4.0])
deque(['cat', 3735928559, 4.0])
deque([3735928559, 4.0])
deque([4.0])

这应该比列表切片更有效:参见deque.popleft() and list.pop(0). Is there performance difference?。还要注意列出切片在 O(k) 时间内工作,其中 k 是切片的长度。

【讨论】:

    【解决方案2】:

    使用collections.deque,正如@jpp 的回答所建议的那样,有时会稍微快一些。 slice 和deque 解决方案的性能相似,并且比例如更好。使用itertools.islice 或仅在list_ 上使用普通索引。

    我试图使版本大致相同,并使用了一个虚拟的func 来计算循环数:

    from __future__ import print_function
    from collections import deque
    from itertools import islice
    from timeit import repeat
    
    import numpy as np
    
    
    list_ = [5, 'cat', 0xDEADBEEF, 4.0]
    list_3k = list_ * 3000
    
    
    def func(x, y):
        return x + 1
    
    
    def f1():
        """list slice"""
        result = 0
        for offset in range(len(list_)):
            for elem in list_[offset:]:
                result = func(result, elem)
        return result
    
    
    def f2():
        """deque"""
        dq = deque(list_)
        result = 0
        for i in range(len(dq)):
            for elem in dq:
                result = func(result, elem)
            dq.popleft()
        return result
    
    
    def f3():
        """itertools slice"""
        result = 0
        for offset in range(len(list_)):
            for elem in islice(list_, offset, None):
                result = func(result, elem)
        return result
    
    
    def f4():
        """basics"""
        result = 0
        n = len(list_)
        for offset in range(n):
            j = offset
            while j < n:
                result = func(result, list_[j])
                j += 1
        return result
    
    
    def timeit(fn, number):
        print("{}: {} loops".format(fn.__name__, fn()))
        times = repeat(fn, repeat=3, number=number)
        print("{:.3f}s ± {:.3f}ms".format(np.mean(times), np.std(times)*1000))
    
    
    if __name__ == "__main__":
        fs = [f1, f2, f3, f4]
    
        for f in fs:
            timeit(f, number=1000000)
    
        list_ = list_3k
        print()
    
        for f in fs:
            timeit(f, number=3)
    

    结果:

    bash-3.2$ python3 foo.py 
    f1: 10 loops
    2.161s ± 9.333ms
    f2: 10 loops
    2.134s ± 5.127ms
    f3: 10 loops
    2.340s ± 11.928ms
    f4: 10 loops
    2.315s ± 4.615ms
    
    f1: 72006000 loops
    23.073s ± 109.857ms
    f2: 72006000 loops
    23.495s ± 596.822ms
    f3: 72006000 loops
    24.432s ± 553.167ms
    f4: 72006000 loops
    40.509s ± 128.367ms
    

    【讨论】:

      猜你喜欢
      • 2011-09-03
      • 2017-11-13
      • 1970-01-01
      • 1970-01-01
      • 2015-10-20
      • 2021-02-23
      • 2020-01-15
      • 2011-11-26
      • 2013-07-21
      相关资源
      最近更新 更多