简体   繁体   English

为什么Python中的列表参数表现得像ByRef?

[英]Why Does List Argument in Python Behave Like ByRef?

This may be for most languages in general, but I'm not sure. 一般来说,这可能适用于大多数语言,但是我不确定。 I'm a beginner at Python and have always worked on copies of lists in C# and VB. 我是Python的初学者,一直致力于C#和VB中的列表副本。 But in Python whenever I pass a list as an argument and enumerate through using a "for i in range," and then change the value of the list argument, the input values actually changes the original list. 但是在Python中,每当我将列表作为参数传递并使用“ for i in range”进行枚举,然后更改list参数的值时,输入值实际上会更改原始列表。 I thought Python was supposed to pass arguments by value by default so that once the function is finished I still have the original values from before I called the function. 我以为Python应该默认通过值传递参数,因此一旦函数完成,我仍然拥有调用该函数之前的原始值。 What am I missing? 我想念什么? Thanks! 谢谢!

Python does pass arguments by value but the value you are receiving is a copy of the reference (incidentally this is the exact same way that C#, VB.NET, and Java behave as well). Python确实通过值传递参数,但是您收到的值是引用的副本(顺便说一下,这与C#,VB.NET和Java的行为完全相同)。

This is the important thing to remember: 这是要记住的重要事项:

Objects are not passed by reference - object references are passed by value . 引用不传递对象-引用按值传递对象

Since you have a copy of the reference, any operation on what that reference points to will be just as if you were holding the original reference itself. 由于您具有参考的副本,因此对该参考所指向的内容进行的任何操作都将如同您拥有原始参考本身一样。

Python -- just like Java does for anything but primitive scalars, and like C# and VB.NET do for the default kind parameters as opposed to boxed types and out / ref parms -- passes "by object reference" (search for that phrase here -- it's how Guido, Python's architect and creator, uses to explain this argument-passing concept). Python –就像Java除了原始标量之外,对其他所有东西都适用,而C#和VB.NET与默认类型参数(而不是盒装类型和out / ref parms)一样,都通过“按对象引用”传递( 在此处搜索该短语) -这就是Python的架构师和创建者Guido用来解释这个参数传递概念的方式。

Every name is a reference to some object; 每个名称都是对某个对象的引用; passing a name (or any other expression) as an argument is just creating yet another reference to the same object (which the function body can access through the parameter's name). 传递名称(或任何其他表达式)作为参数只是在创建对同一对象的另一个引用(函数主体可以通过参数的名称来访问该对象)。 ((There's no such thing as "a reference to a name": there are names , which are one kind of reference to objects, and object -- period)). ((没有“对名称的引用”之类的东西:存在名称 ,这是对对象的一种引用,即对象-句点))。

When you're passing a mutable object, ie one which has mutating methods (like for example a list), the called function can mutate the object by calling, directly or indirectly, its mutating methods. 当您传递可变对象时,即具有可变方法(例如列表)的对象时,被调用函数可以通过直接或间接调用其可变方法来使对象可变。 ((By "indirectly", I mean "through operators" -- for example: ((((“间接”是指“通过运算符”,例如:

somelist[len(somelist):] = [whatever]

is exactly identical to somelist.append(whatever) .)) somelist.append(whatever)完全相同。))

When you want to pass a list into a function, but do not want the function to be able to mutate that list in any way, you must pass a copy of the list instead of the original -- just like in Java, C#, VB.NET. 当您希望将列表传递给函数但希望函数能够以任何方式使该列表发生变异时,则必须传递列表的副本而不是原始副本 ,就像在Java,C#,VB中一样。净。

Be very clear about the distinction between rebinding a name and mutating an object . 关于重新绑定名称更改对象之间的区别要非常清楚。 Rebinding the name ("barename", that is -- qualified-names are different;-) only affects that name -- NOT any object whatsoever. 重新绑定名称(“裸名”,即-限定名称不同;-) 影响该名称 - 不会影响任何对象。 For example: 例如:

def f1(alist):
  alist = [23]

def f2(alist):
  alist[:] = [23]

Can you spot the difference between these two functions? 您能发现这两个功能之间的区别吗? One is rebinding the barename alist -- without any effect whatsoever on anything. 一是重新绑定barename alist -没有任何东西上的任何影响。 The other is mutating (altering, changing, ...) the list object it received as its argument -- by setting its content to be a one-item list with an int as its sole item. 另一种是通过将其内容设置为一个以int为唯一项的单项列表来更改(更改,更改等)它作为参数接收的列表对象。 Completely, utterly different things!!! 完全不同的事情!!!

To add to Andrew's answer, you need to explicitly make a copy of a list if you want to retain the original. 要添加到安德鲁的答案中,如果您想保留原始列表,则需要显式复制列表。 You can do this using the copy module, or just do something like 您可以使用copy模块执行此操作,也可以执行类似的操作

a = [1,2]
b = list(a)

Since copying objects usually implies a performance hit, I find it helpful to explicitly use the copy module in my larger projects. 由于复制对象通常意味着性能下降,因此我发现在较大的项目中显式使用复制模块会有所帮助。 That way, I can easily find all the places where I'm going to use a bunch more memory. 这样,我可以轻松找到将要使用更多内存的所有位置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM