据我所知,在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]
将整数打包在一个元素列表中。但必须有更好的方法。
答案 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 += something
与a = 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)
其次,理解基本上等同于map
和filter
,但使用表达式而不是函数。如果您的表达只是“调用此函数”,请使用map
或filter
。如果你的函数只是一个“评估这个表达式”的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]