Python:多次创建一个小列表有多贵?

时间:2010-03-09 13:56:31

标签: python

我在Python中一遍又一遍地遇到以下小烦恼的困境:

选项1:

如果为do_something()的每次调用重新创建a_list,那么如果多次调用,则更干净但更慢(?)

def do_something():    
  a_list = ["any", "think", "whatever"]    
  # read something from a_list

选项2:

Uglier但效率更高(再次创建a_list创建)

a_list = ["any", "think", "whatever"]    
def do_something():    
  # read something from a_list

您怎么看?

8 个答案:

答案 0 :(得分:16)

有什么难看的?

列表的内容是否始终是常量,如示例中所示?如果是这样的话:Python的最新版本(从2.4开始)将通过评估常量表达式并保持结果来优化它,但前提是它只是一个元组。所以你可以把它变成一个元组。或者你可以不再担心像这样的小事。

这是一个常量列表和一个常量元组:

>>> def afunc():
...    a = ['foo', 'bar', 'zot']
...    b = ('oof', 'rab', 'toz')
...    return
...
>>> import dis; dis.dis(afunc)
  2           0 LOAD_CONST               1 ('foo')
              3 LOAD_CONST               2 ('bar')
              6 LOAD_CONST               3 ('zot')
              9 BUILD_LIST               3
             12 STORE_FAST               0 (a)

  3          15 LOAD_CONST               7 (('oof', 'rab', 'toz'))
             18 STORE_FAST               1 (b)

  4          21 LOAD_CONST               0 (None)
             24 RETURN_VALUE
>>>

答案 1 :(得分:4)

如果不需要,不要创建多次。这是一个简单的优化,可以在你身上完成,我个人根本没有找到第二个例子。

有些人可能会争辩不要担心优化这样的小事情,但我觉得应该立即完成这个简单易行的事情。我不愿意看到你的应用程序创建了多个副本,它们不需要简单地保留任意“代码美”。 :)

答案 2 :(得分:4)

选项3:

def do_something(a_list = ("any", "think", "whatever")):
    read something from a_list

选项3与选项1相比:

在我看来,两者都具有相同的可读性(尽管有些人在评论中似乎有不同的想法!:-))。您甚至可以像这样编写选项3

def do_something(
    a_list = ("any", "think", "whatever")):
    read something from a_list

这确实最小化了可读性方面的差异。 但是,与选项1不同,选项3仅定义a_list一次 - 在定义do_something时。这正是我们想要的。

选项3与选项2相比:

尽可能避免使用全局变量。选项3允许您这样做。 此外,使用选项2,随着时间的推移或其他人维护此代码,a_list的定义可能会与def do_something分开。这可能不是什么大问题,但我认为这有点不可取。

答案 3 :(得分:3)

如果a_list没有变化,请将其移出功能。

答案 4 :(得分:2)

  1. 您有一些数据
  2. 你有一个与之相关的方法
  3. 除非必须,否则您不希望为了优化方法的速度而全局保存数据。
  4. 我认为这就是课程的目的。

    class Processor:
        def __init__(this):
            this.data = "any thing whatever".split()
        def fun(this,arg):
            # do stuff with arg and list
    
    inst = Processor()
    inst.fun("skippy)
    

    另外,如果您有一天想要将数据分离到文件中,您只需修改构造函数即可。

答案 5 :(得分:1)

好吧,它似乎归结为在函数中初始化数组:

import time
def fun1():
        a = ['any', 'think', 'whatever']
        sum = 0
        for i in range(100):
                sum += i

def fun2():
        sum = 0
        for i in range(100):
                sum += i


def test_fun(fun, times):
        start = time.time()
        for i in range(times):
                fun()
        end=time.time()
        print "Function took %s" % (end-start)

# Test
print 'warming up'
test_fun(fun1, 100)
test_fun(fun2, 100)

print 'Testing fun1'
test_fun(fun1, 100000)
print 'Testing fun2'
test_fun(fun2, 100000)

print 'Again'
print 'Testing fun1'
test_fun(fun1, 100000)
print 'Testing fun2'
test_fun(fun2, 100000)

和结果:

>python test.py
warming up
Function took 0.000604152679443
Function took 0.000600814819336
Testing fun1
Function took 0.597407817841
Testing fun2
Function took 0.580779075623
Again
Testing fun1
Function took 0.595198154449
Testing fun2
Function took 0.580571889877

看起来没什么区别。

答案 6 :(得分:0)

如果从未修改过列表,为什么要使用列表?

在不知道您的实际要求的情况下,我建议您只使用一些if语句来完全删除列表和“从列表中读取内容”部分。

答案 7 :(得分:0)

我一直致力于自动化系统,每天处理100,000,000条记录,其中1%的性能提升是巨大的。

我在这个系统上学到了一个很大的教训:更快更好,但只有当你知道什么时候它足够快时。

1%的改进可能会大大缩短总处理时间,但是当我们需要下一次硬件升级时,这还不够。我的应用程序如此之快,以至于我花在过去的1%上花费的时间可能比新服务器的成本高。

在您的情况下,您必须先调用do_something数万次才能在性能上产生显着差异。在某些情况下,这会产生影响,在其他情况下则不会。