Python for循环中的Python变量赋值

时间:2014-01-03 22:13:00

标签: python variables loops

据我所知,在Python中,常规的c ++样式变量赋值被替换为对东西的引用,即

a=[1,2,3]
b=a
a.append(4)
print(b)       #gives [1,2,3,4]
print(a)       #gives [1,2,3,4]

但我仍然困惑为什么与基本类型的类似情况,例如。整数的工作方式不同?

a=1
b=a
a+=1
print(b)          # gives 1
print(a)          # gives 2

但等等,当我们考虑循环时,它会变得更加混乱!

li=[1,2,3]
for x in li:
    x+=1
print(li)     #gives [1,2,3]

这是我的预期,但如果我们这样做会发生什么:

a,b,c=1,2,3
li=[a,b,c]
for x in li:
    x+=1
print(li)        #gives [1,2,3]

也许我的问题应该是如何遍历整数列表并在没有map()的情况下更改它们,因为我需要一个if语句。我唯一能做的就是

for x in range(len(li)):
    Do stuff to li[x]

将整数打包在一个元素列表中。但必须有更好的方法。

6 个答案:

答案 0 :(得分:6)

嗯,您需要考虑可变不可变类型。

对于列表,它是可变的。 对于整数,它是不可变的,这意味着如果更改它将引用新对象。执行 a + = 1时, a 将被分配一个新对象,但 b 仍然指向同一个对象。

答案 1 :(得分:5)

a=[1,2,3]
b=a
a.append(4)
print(b)       #[1,2,3,4]
print(a)       #[1,2,3,4]

您在此处修改列表。列表内容会更改,但列表标识仍然存在。

a=1
b=a
a+=1

然而,这是一个重新分配。您将另一个对象分配给a

请注意,如果您在第一个示例中执行了a += [4],则会看到相同的结果。这是因为a += somethinga = a.__iadd__(something)相同,如果a = a.__add__(something)不存在则回退到__iadd__()

不同之处在于__iadd__()尝试通过修改它所处理的对象并返回它来完成其工作" inplace"。所以a指的是和以前一样的。这仅适用于列表等可变对象。

调用onts __add__()等不可变对象。它返回一个不同的对象,导致a指向另一个对象。没有其他选择,因为整数是不可改变的。


a,b,c=1,2,3
li=[a,b,c]
for x in li:
    x+=1
print(li)        #[1,2,3]

此处x += 1表示与x = x + 1相同。它会更改x引用的位置,但不会更改列表内容。

  

也许我的问题应该是如何遍历整数列表并在没有> map()的情况下更改它们,因为我需要if语句。

for i, x in enumerate(li):
    li[i] = x + 1

为每个列表位置分配旧值+ 1。

答案 2 :(得分:2)

对于C ++ - 最简单的认为每个Python对象都是一个指针。当你写a = [1, 2, 3]时,你基本上写List * a = new List(1, 2, 3)。当您撰写a = b时,您基本上会写List * b = a

但是当您从列表中取出实际项目时,这些项目恰好是数字。数字是不可改变的;持有指向不可变对象的指针与按值保持该对象一样好。

所以你的for x in a: x += 1基本上是

 for (int x, it = a.iterator(); it->hasMore(); x=it.next()) {
    x+=1; // the generated sum is silently discarded
 }

显然没有效果。

如果列表元素是可变对象,您可以完全按照您编写的方式改变它们。参见:

a = [[1], [2], [3]] # list of lists
for x in a:  # x iterates over each sub-list
  x.append(10)
print a  # prints [[1, 10], [2, 10], [3, 10]]

但除非你有令人信服的理由(例如,在大量内存负载下有数百万个对象的列表),否则最好制作一份列表副本,应用转换和可选的过滤器。这可以通过列表理解:

轻松完成
a = [1, 2, 3, 0]
b = [n + 1 for n in a] # [2, 3, 4, 1]
c = [n * 10 for n in a if n < 3] # [10, 20, 0]

或者,你可以编写一个显式循环来创建另一个列表:

source = [1, 2, 3]
target = []
for n in source:
  n1 = <many lines of code involving n>
  target.append(n1)

答案 3 :(得分:2)

这里重要的是变量 names 。它们实际上只是字典的关键。它们在运行时解析,具体取决于当前范围。

让我们看一下您在代码中访问的名称。 locals函数帮助我们:它显示了本地范围内的名称(及其值)。这是你的代码,带有一些调试输出:

a = [1, 2, 3]       # a is bound
print(locals())
for x in a:         # a is read, and for each iteration x is bound
    x = x + 3       # x is read, the value increased and then bound to x again
    print(locals())
print(locals())
print(x)

(注意我将x += 3扩展为x = x + 3以提高名称访问的可见性 - 读取和写入。)

首先,将列表[1, 2, 3]绑定到名称a。然后,迭代列表。在每次迭代期间,该值都绑定到当前范围中的名称x。然后,您的作业会将另一个值分配给x

这是输出

{'a': [1, 2, 3]}
{'a': [1, 2, 3], 'x': 4}
{'a': [1, 2, 3], 'x': 5}
{'a': [1, 2, 3], 'x': 6}
{'a': [1, 2, 3], 'x': 6}
6

您无法访问列表a,因此永远不会修改它。

要解决您的问题,我将使用枚举函数获取索引以及值,然后使用名称a访问列表以进行更改。

for idx, x in enumerate(a):
    a[idx] = x + 3
print(a)

输出:

[4, 5, 6]

请注意,您可能希望将这些示例包装在函数中,以避免混乱的全局命名空间。

有关范围的更多信息,请阅读chapter in the Python tutorial。要进一步研究,请使用globals函数查看全局命名空间的名称。 (不要与global关键字混淆,请注意缺少的's'。)

玩得开心!

答案 4 :(得分:1)

您的问题有多个部分,因此一个答案很难涵盖所有部分。 glglgl在大部分方面做得很好,但你的最后一个问题仍然无法解释:

  

也许我的问题应该是如何遍历整数列表并在没有map()的情况下更改它们,因为我需要一个if语句

“我在那里需要一个if语句”并不意味着你不能使用map


首先,如果您希望if选择要保留的值,map会有一位名为filter的好朋友。例如,要仅保留奇数,但每个都添加一个,您可以这样做:

>>> a = [1, 2, 3, 4, 5]
>>> b = []
>>> for x in a:
...     if x%2:
...         b.append(x+1)

或者只是这个:

>>> b = map(lambda x: x+1, filter(lambda x: x%2, a))

另一方面,如果您希望if控制表达式本身 - 例如,要将奇数加1,但只保留偶数,则可以使用if表达式与使用if语句的方式相同:

>>> for x in a:
...     if x%2:
...         b.append(x+1)
...     else:
...         b.append(x)

>>> b = map(lambda x: x+1 if x%2 else x, a)

其次,理解基本上等同于mapfilter,但使用表达式而不是函数。如果您的表达只是“调用此函数”,请使用mapfilter。如果你的函数只是一个“评估这个表达式”的lambda,那么使用一个理解。以上两个例子以这种方式更具可读性:

>>> b = [x+1 for x in a if x%2]
>>> b = [x+1 if x%2 else x for x in a]

答案 5 :(得分:0)

您可以执行以下操作:li = [x+1 for x in li]