这样的代码经常发生:

l = []
while foo:
    # baz
    l.append(bar)
    # qux

如果您要向列表中添加数千个元素,这将非常缓慢,因为列表必须不断调整大小以适应新元素。

在Java中,可以创建具有初始容量的ArrayList。如果你知道你的清单有多大,这将会更有效率。

我知道这样的代码通常可以被重构成一个列表理解式。但是,如果for/while循环非常复杂,这是不可行的。对于我们Python程序员来说,是否也有类似的方法?


当前回答

对于某些应用程序,字典可能是您正在寻找的。例如,在find_totient方法中,我发现使用字典更方便,因为我没有零索引。

def totient(n):
    totient = 0

    if n == 1:
        totient = 1
    else:
        for i in range(1, n):
            if math.gcd(i, n) == 1:
                totient += 1
    return totient

def find_totients(max):
    totients = dict()
    for i in range(1,max+1):
        totients[i] = totient(i)

    print('Totients:')
    for i in range(1,max+1):
        print(i,totients[i])

这个问题也可以用预分配的列表来解决:

def find_totients(max):
    totients = None*(max+1)
    for i in range(1,max+1):
        totients[i] = totient(i)

    print('Totients:')
    for i in range(1,max+1):
        print(i,totients[i])

我觉得这不是很优雅,而且容易产生错误,因为我存储的是None,如果我不小心错误地使用它们,它可能会抛出异常,而且因为我需要考虑映射让我避免的边缘情况。

没错,字典的效率不会那么高,但正如其他人评论的那样,速度上的微小差异并不总是值得冒重大维护风险。

其他回答

如果使用NumPy,就会出现Python中的预分配问题,因为NumPy有更多类似c的数组。在这种情况下,预分配关注的是数据的形状和默认值。

如果要在大量列表上进行数值计算并希望获得性能,可以考虑NumPy。

根据我的理解,Python列表已经非常类似于数组列表。但如果你想调整这些参数,我在互联网上找到了这篇文章,可能会很有趣(基本上,只需要创建自己的ScalableList扩展):

http://mail.python.org/pipermail/python-list/2000-May/035082.html

警告:这个答案有争议。看到评论。

def doAppend( size=10000 ):
    result = []
    for i in range(size):
        message= "some unique object %d" % ( i, )
        result.append(message)
    return result

def doAllocate( size=10000 ):
    result=size*[None]
    for i in range(size):
        message= "some unique object %d" % ( i, )
        result[i]= message
    return result

结果。(计算每个函数144次,平均时间)

simple append 0.0102
pre-allocate  0.0098

结论。这无关紧要。

过早的优化是万恶之源。

正如其他人所提到的,预播种列表的最简单方法是使用NoneType对象。

话虽如此,在决定这是必要的之前,您应该了解Python列表的实际工作方式。

在列表的CPython实现中,底层数组总是创建有开销空间,大小逐渐增大(4、8、16、25、35、46、58、72、88、106、126、148、173、201、233、269、309、354、405、462、526、598、679、771、874、990、1120等),因此调整列表的大小几乎不会经常发生。

由于这种行为,大多数list.append()函数的追加复杂度都是O(1),只有在跨越其中一个边界时复杂度才会增加,此时复杂度将为O(n)。在S.Lott的答案中,这种行为导致了执行时间的最小增加。

来源:Python列表实现

对于某些应用程序,字典可能是您正在寻找的。例如,在find_totient方法中,我发现使用字典更方便,因为我没有零索引。

def totient(n):
    totient = 0

    if n == 1:
        totient = 1
    else:
        for i in range(1, n):
            if math.gcd(i, n) == 1:
                totient += 1
    return totient

def find_totients(max):
    totients = dict()
    for i in range(1,max+1):
        totients[i] = totient(i)

    print('Totients:')
    for i in range(1,max+1):
        print(i,totients[i])

这个问题也可以用预分配的列表来解决:

def find_totients(max):
    totients = None*(max+1)
    for i in range(1,max+1):
        totients[i] = totient(i)

    print('Totients:')
    for i in range(1,max+1):
        print(i,totients[i])

我觉得这不是很优雅,而且容易产生错误,因为我存储的是None,如果我不小心错误地使用它们,它可能会抛出异常,而且因为我需要考虑映射让我避免的边缘情况。

没错,字典的效率不会那么高,但正如其他人评论的那样,速度上的微小差异并不总是值得冒重大维护风险。