len(List)的性能与读取一个变量相比如何?

11
一个类似的问题已经在这里Cost of len() function被问到了。但是,这个问题看起来关注的是len本身的代价。 假设我有一个重复多次使用len(List)的代码,每次都是O(1),读取变量也是O(1),加上赋值也是O(1)
顺便说一下,我发现n_files = len(Files)比我的代码中反复出现的len(Files)更易读。所以,这已经是我这样做的一个动力了。 你还可以反对我,说在代码的某个地方Files可能会被修改,所以n_files不再正确,但实际情况并非如此。
我的问题是: 在调用len(Files)的次数之后,访问n_files会更快吗?

1
考虑到Python每次都需要查找len,所以这个数字可能是1。但为什么不进行一些测试来验证呢?由于两种方法都是O(1),因此这完全取决于固定成本。 - jonrsharpe
我认为与创建新的“int”实例相比,“len”会更便宜... - oz123
@jonrsharpe 那么这样做len_func = len,然后调用len_func(Files)怎么样? - DeepSpace
@DeepSpace 这是一个好问题 - 本地别名函数是提高性能的一种方式,这可能会使比较更紧密。不过还有函数调用 - jonrsharpe
1
@PadraicCunningham,因为O(1)可能是5毫秒或500毫秒,我正在努力更好地理解Python,而不仅仅是成为一个“编码者”... 在这里提出这个问题也有助于其他人... - oz123
显示剩余5条评论
4个回答

14

以下是在Windows 7上使用Python 2.7.10对一个包含10个元素的列表进行100万次调用的时间结果(单位为秒):store表示我们是否存储长度或持续调用len,而alias表示我们是否创建本地别名以供len使用:

Store Alias n=      1      10     100
Yes   Yes       0.862   1.379   6.669
Yes   No        0.792   1.337   6.543
No    Yes       0.914   1.924  11.616
No    No        0.879   1.987  12.617

还有一个包含一千个元素的列表:

Store Alias n=      1      10     100
Yes   Yes       0.877   1.369   6.661
Yes   No        0.785   1.299   6.808
No    Yes       0.926   1.886  11.720
No    No        0.891   1.948  12.843

结论:

  • 存储结果比重复调用 len 更有效率,即使在 n == 1 的情况下也是如此;
  • len 创建本地别名可以在较大的 n(不存储结果)时稍微改善性能,但并不像存储结果那样有效;
  • 列表长度对性能的影响可忽略,这表明整数是否被内部化没有任何区别。

测试脚本:

def test(n, l, store, alias):
    if alias:
        len_ = len
        len_l = len_(l)
    else:
        len_l = len(l)
    for _ in range(n):
        if store:
            _ = len_l
        elif alias:
            _ = len_(l)
        else:
            _ = len(l)

if __name__ == '__main__':
    from itertools import product
    from timeit import timeit
    setup = 'from __main__ import test, l'
    for n, l, store, alias in product(
        (1, 10, 100),
        ([None]*10,),
        (True, False),
        (True, False),
    ):
        test_case = 'test({!r}, l, {!r}, {!r})'.format(n, store, alias)
        print test_case, len(l),
        print timeit(test_case, setup=setup)

2

在Python中,函数调用是比较耗时的,因此如果您确定在访问变量的长度时,n_files 的大小不会发生改变,那么您可以使用该变量,如果这对您来说更易读的话。

对于访问len(list)和从变量访问的性能测试示例,结果如下:

In [36]: l = list(range(100000))

In [37]: n_l = len(l)

In [40]: %timeit newn = len(l)
10000000 loops, best of 3: 92.8 ns per loop

In [41]: %timeit new_n = n_l
10000000 loops, best of 3: 33.1 ns per loop

访问变量比使用 len() 始终更快。

1

使用len(Files)而不是n_files可能会更慢。是的,你必须查找n_files,但在前一种情况下,你将不得不查找lenFiles,然后在此基础上调用一个“计算”Files长度的函数。


你觉得这个回答补充了什么? - jonrsharpe
@jonrsharpe 这是一个理论上的解释,为什么它应该更快。其中两个答案或多或少只是实验测试,以查看哪个最快,而Anands则指出函数调用是昂贵的(这可能是真的)。 - skyking
1
目前,Python 不会计算列表的长度。它作为对象内的变量存储。 https://github.com/python/cpython/blob/2.5/Objects/listobject.c#L379 - DonCarleone
@DonCarleone 在这种情况下,“计算”可以被视为仅是O(1)操作。 - fdermishin
@fdermishin 计算 /ˈkalkyəˌlāt/ 动词 1. 通过数学方法确定(某物的数量或数字)。 - DonCarleone

1
使用l = len(li)更快:
python -m timeit -s "li = [1, 2, 3]" "len(li)"
1000000 loops, best of 3: 0.239 usec per loop

python -m timeit -s "li = [1, 2, 3]; l = len(li)" "l"
10000000 loops, best of 3: 0.0949 usec per loop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接