在python

时间:2015-06-04 02:48:55

标签: python list list-comprehension

假设我有一个我希望增加的数字列表,我只对递增的值感兴趣,而不是之后的原始值。最简单的方法是原位不复制列表?

确实

a = [1, 2, 3]
a = [i+1 for i in a]

导致a的中间副本,或者python解释器是否优化了这个?

不幸的是,我的python知识仍然是肤浅的。我的母语是C ++。

6 个答案:

答案 0 :(得分:4)

如果不复制列表,您可以执行此操作:

In [1]: a = [1, 2, 3]

In [2]: id(a)
Out[2]: 48701592

In [3]: for i in xrange(len(a)): # range(len(a)) for python 3
   ...:     a[i] += 1
   ...:     

In [4]: a
Out[4]: [2, 3, 4]

In [5]: id(a)
Out[5]: 48701592

答案 1 :(得分:3)

列表中的整数有一个问题:这些实际上是不可变对象,所以如果你增加一个整数,它实际上会被一个新对象重新调整。

因此,虽然列表解析创建一个中间副本,但开销可能不是那么大的问题。如果有疑问,您可以使用timeit来分析备选方案。

如果列表中还有其他引用,则会出现一个经常被忽视的问题:

>>> a = [1,2,3,4]
>>> b = a
>>> for i in xrange(len(a)):   # range() in Python 3!!
...     a[i] += 1
... 
>>> a
[2, 3, 4, 5]
>>> b
[2, 3, 4, 5]

如果为a创建新列表,b当然不会看到这些修改:

>>> a = [1,2,3,4]
>>> b = a
>>> a = [i + 1 for i in a]
>>> a
[2, 3, 4, 5]
>>> b
[1, 2, 3, 4]

所以,答案实际上是:它取决于。使用for循环

答案 2 :(得分:2)

使用numpy数组的另一个选项

import numpy as np

A=np.array([1,2,3])
A=A+1

答案 3 :(得分:0)

我相信(但我是错误 - 请参阅注释),避免复制列表的正确方法是使用切片赋值和生成器表达式:

  Actions act = new Actions(driver);
  WebElement onElement = driver.findElement(By.linkText("Gmail"));
  act.contextClick(onElement).perform();
  act.sendKeys("w").perform(); 

使用列表推导a[:] = (i+1 for i in a) 的问题在于它将首先构建一个新列表,然后切片赋值将遍历临时列表以将[i+1 for i in a]的每个元素绑定到该值。

使用生成器表达式,当切片分配请求其他值时,懒惰地遍历现有列表,因此原则上不需要创建临时列表。但是 - 正如我更正的那样 - 切片分配会导致从生成器表达式创建列表。

答案 4 :(得分:-1)

Ans to Q#1 - 一旦我们进一步操作数组(或数组元素),它就像对原始数组的引用,并对其进行持久的新更改。它可以被称为intermediate副本,但显然不是原始数组的副本。

Ans to Q#2 - Python内部不会优化a,如问题中所述。

虽然您可以通过以下方式获得所需的结果:

a = [1, 2, 3]
a = [i+1 for i in a]
print a

仍然使用 slicing 符号 : 非常合适:

a[:] = [i+1 for i in a]
print a

***感谢@Paul Rooney分享这个例子。

答案 5 :(得分:-2)

要增加列表中的每个元素而不创建列表的另一个副本,您应该按照@ Akavall的回答。

如果列表理解创建了列表的中间副本,请回答您的问题,以下是解释。

列表理解

a = [i+1 for i in a]

可以扩展为:

b = []
for i in a:
    b.append(i+1)
a = b

如您所见,我们遍历列表并逐个递增每个元素。这些元素存储在临时列表中,新列表将分配回a

在这种情况下,a只是引用列表的标签。 a = b只是更新引用以指向新列表。