这可能适用于大多数语言,但我不确定。我是 Python 的初学者,一直致力于 C# 和 VB 中的列表副本。但是在 Python 中,每当我将列表作为参数传递并通过使用“for i in range”进行枚举,然后更改列表参数的值时,输入值实际上会更改原始列表。我认为 Python 应该默认按值传递参数,这样一旦函数完成,我仍然拥有调用函数之前的原始值。我错过了什么?谢谢!
3 回答
Python 确实按值传递参数,但您收到的值是引用的副本(顺便说一下,这与 C#、VB.NET 和 Java 的行为方式完全相同)。
这是要记住的重要事项:
对象不是按引用传递的——对象引用是按值传递的。
由于您拥有参考的副本,因此对该参考指向的任何操作都将就像您持有原始参考本身一样。
Python——就像 Java 对原始标量所做的任何事情一样,就像 C# 和 VB.NET 对默认类型参数所做的那样,而不是盒装类型和 out / ref 参数——传递“按对象引用”(在此处搜索该短语——这就是 Python 的架构师和创建者 Guido 用来解释这个参数传递概念的方式)。
每个名称都是对某个对象的引用;将名称(或任何其他表达式)作为参数传递只是创建对同一对象的另一个引用(函数体可以通过参数的名称访问该对象)。((没有“对名称的引用”之类的东西:有names,这是对对象的一种引用,还有 object --句号))。
当您传递一个可变对象时,即具有可变方法的对象(例如列表),被调用函数可以通过直接或间接调用其可变方法来改变对象。((通过“间接”,我的意思是“通过运营商”——例如:
somelist[len(somelist):] = [whatever]
somelist.append(whatever)
与.))完全相同
当您想将列表传递给函数,但不希望函数能够以任何方式改变该列表时,您必须传递列表的副本而不是原始列表——就像在 Java、C#、VB 中一样。网。
非常清楚重新绑定名称和改变对象之间的区别。重新绑定名称(“barename”,即 - 限定名称不同;-)仅影响该名称-不影响任何对象。例如:
def f1(alist):
alist = [23]
def f2(alist):
alist[:] = [23]
你能看出这两个函数的区别吗?一个是重新绑定barename—— alist
对任何东西都没有任何影响。另一个是改变(改变、改变……)它作为参数接收的列表对象——通过将其内容设置为一个单项列表,其中一个 int 作为其唯一项。 完全不同的东西!!!
要添加到 Andrew 的答案,如果您想保留原件,您需要明确制作一份列表的副本。您可以使用copy
模块执行此操作,或者只是执行类似的操作
a = [1,2]
b = list(a)
由于复制对象通常意味着性能下降,我发现在我的大型项目中明确使用复制模块很有帮助。这样,我可以很容易地找到我将要使用更多内存的所有地方。