Python:多次创建一个小列表的成本有多高?

Python:多次创建一个小列表的成本有多高?,python,Python,在Python中,我一次又一次地遇到以下恼人的小难题: 备选案文1: 更干净但速度较慢(?)如果多次调用,因为每次调用do_something()都会重新创建一个_列表 备选案文2: 更丑陋但更高效(避免再次创建a_列表) 你觉得怎么样?如果你不需要的话,永远不要创建一次以上的东西。这是一个简单的优化,可以做你的一部分,我个人并不觉得第二个例子丑陋 有些人可能会争辩说,不要担心优化像这样的小事情,但我觉得应该立即做一些这么简单的事情来修复。我不希望看到您的应用程序创建任何东西的多个副本,而这些东

在Python中,我一次又一次地遇到以下恼人的小难题:

备选案文1:

更干净但速度较慢(?)如果多次调用,因为每次调用do_something()都会重新创建一个_列表

备选案文2:

更丑陋但更高效(避免再次创建a_列表)


你觉得怎么样?

如果你不需要的话,永远不要创建一次以上的东西。这是一个简单的优化,可以做你的一部分,我个人并不觉得第二个例子丑陋


有些人可能会争辩说,不要担心优化像这样的小事情,但我觉得应该立即做一些这么简单的事情来修复。我不希望看到您的应用程序创建任何东西的多个副本,而这些东西不需要简单地保留任意的“代码美”。)

如果您的
a_列表
没有更改,请将其移出功能。

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

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

选项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_列表
——在定义
do_something
时。这正是我们想要的

选项3与选项2相比:

尽可能避免使用全局变量。选项3允许您这样做。 此外,使用选项2,随着时间的推移或如果其他人维护此代码,
a_列表
的定义可能会与
def do_something
分离。这可能没什么大不了的,但我认为这有点不受欢迎。

这有什么难看的

列表的内容是否总是常量,如您的示例所示?如果是这样的话: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
>>>

那么,它似乎可以归结为是否在函数中初始化数组:

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

看起来没有什么区别。

我曾在每天处理100000000多条记录的自动化系统上工作过,其中1%的性能改进是巨大的

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

1%的改进将大大减少总的处理时间,但这还不足以影响我们需要下一次硬件升级的时间。我的应用程序速度非常快,我花在挤奶上的时间可能比一台新服务器花费的时间还要多

在您的情况下,您必须调用do_something数万次,然后才能显著提高性能。在某些情况下,这会产生影响,而在另一些情况下则不会

  • 你有一些数据
  • 您有一个与之关联的方法
  • 您不希望为了优化方法的速度而全局保留数据,除非必须这样做
  • 我想这就是上课的目的

    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)
    

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

    真的吗?我不知道。元组被优化为只创建一次。有意思。。这可能会解决90%的案件OP@AnonymousDriveByDownVoter:想留下评论吗?Gneee。。。抱歉,将列表视为默认参数是有害的。-1“尽可能避免全局变量”是合理的建议,但“Python是强制[d]首先在locals()dict中搜索变量”是完全的codswallop。编译器知道它是本地的还是全局的,并且在选项2的情况下发出一条LOAD global指令。当且仅当您调用locals()函数时,才会显示locals()dict——编译器知道所有局部变量的存储位置;没有dict用于运行正常的函数/方法代码。如果有疑问,请选择最可读、最优雅、最自文档化的代码,直到性能分析器告诉您不要这样做。是的,我知道。但这是一件很小、很烦人、很容易避免的事情……“小”的意思是忽略它。做最清楚的事。把性能考虑放在一边,直到你能证明这是一个问题。根据我的回答,使用类。Data+方法就是它们的用途。如果您在某个地方调用do_fun1(),可能会有所帮助。;-)将其嵌套在函数中完全是另一回事。在我们正在讨论的条件下尝试:(1)局部(2)全局(3)默认参数(4)局部,但作为元组而不是列表。另外,很大一部分时间都花在for循环上,这与for循环无关。扔掉循环,并抛出一个选项(0)根本没有列表/元组,然后报告(1)-(0)、(2)-(0)等等
    >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
    
    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)