难以理解 Python 中传递值和引用
遇到对象何时更改以及何时不在 Python 中的问题。下面是我设计得很糟糕的示例:
class person:
age = 21
class bar:
def __init__(self, arg1):
self.foo = arg1
self.foo.age = 23
def baz(arg1):
arg1.age = 27
def teh(arg1):
arg1 = [3,2,1]
Person1 = person()
bar1 = bar(Person1)
print Person1.age
print bar1.foo.age
baz(Person1)
print Person1.age
print bar1.foo.age
meh = [1,2,3]
teh(meh)
print meh
输出为
23
23
27
27
[1, 2, 3]
因此,当我们声明 Person1 时,Person1.age 为 21。对此对象的引用将传递给 bar 的另一个类实例(称为 bar1)的类构造函数。对此引用所做的任何更改都会更改 Person1。
当我们将 Person1 传递给普通函数时也是这种情况,Person1.age 现在等于 27。
但是为什么这对变量“meh”不起作用?当然,如果我们赋值一个变量a = meh
并改变a = [6, 6, 6]
,那么meh也会改变。我很困惑。有没有关于这一切如何运作的文献?
Having issues with when objects are changed and when they aren't in Python. Here is my poorly contrived example below:
class person:
age = 21
class bar:
def __init__(self, arg1):
self.foo = arg1
self.foo.age = 23
def baz(arg1):
arg1.age = 27
def teh(arg1):
arg1 = [3,2,1]
Person1 = person()
bar1 = bar(Person1)
print Person1.age
print bar1.foo.age
baz(Person1)
print Person1.age
print bar1.foo.age
meh = [1,2,3]
teh(meh)
print meh
The output is
23
23
27
27
[1, 2, 3]
So when we declare Person1, Person1.age is 21. A reference to this object is passed to the class constructor of another class instance of bar, called bar1. Any changes made to this reference will change Person1.
This is also the case when we pass Person1 to a normal function, the Person1.age now equals 27.
But why doesn't this work on the variable "meh"? Certainly, if we assign a variable a = meh
and change a = [6, 6, 6]
, then meh will also be changed. I'm confused. Is there any literature on how all this works?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我可以看到三个基本的 Python 概念可以阐明这个问题:
1)首先,从像 in 这样的可变对象进行赋值
就像复制指针(而不是指向的值):
self.foo 和 arg1 是同一个对象。这就是接下来的行
修改
arg1
(即Person1
)的原因。 变量因此是可以指向唯一对象的不同“名称”或“标签”(此处为person
对象)。这解释了为什么baz(Person1)
将Person1.age
和bar1.foo.age
修改为 27,因为 < code>Person1 和bar1.foo
只是相同person
对象的两个名称(Person1 是 bar1.foo
返回True
,在 Python 中)。2)第二个重要的概念是作业。变量
arg1
是本地变量,因此代码首先执行
arg1 = meh
,这意味着arg1
是列表的附加(本地)名称嗯
;但执行arg1 = [3, 2, 1]
就像在说“我改变了主意:arg1
从现在开始将是一个new的名称em> 列表,[3,2,1]”。这里要记住的重要一点是,尽管用“等号”表示,但赋值是不对称的:它们为右侧的(可变)对象提供了一个附加名称,在左侧给出(这就是为什么你不能这样做[3, 2, 1] = arg1
,因为左侧必须是一个名称[或多个名称])。所以,arg1 = meh; arg1 = [3, 2, 1] 无法更改meh
。3)最后一点与问题标题相关:“按值传递”和“按引用传递”不是Python中相关的概念。相关概念是“可变对象”和“不可变对象”。列表是可变的,而数字则不然,这解释了您所观察到的情况。此外,您的
Person1
和bar1
对象是可变的(这就是您可以更改此人的年龄的原因)。您可以在文本教程和视频教程。维基百科还有一些(更多技术性)信息。一个示例说明了可变和不可变之间的行为差异:最后一行不相当于
y = y + [1, 2, 3]
,因为这只会放置一个变量y
中的新列表对象,而不是更改y
和x
引用的列表。上面的三个概念(变量作为名称[用于可变对象]、不对称赋值和可变性/不可变性)解释了 Python 的许多行为。
I can see three fundamental Python concepts that can shine some light on the question:
1) First, an assignment from a mutable object like in
is like copying a pointer (and not the value pointed to):
self.foo
andarg1
are the same object. That's why the line that follows,modifies
arg1
(i.e.Person1
). Variables are thus different "names" or "labels" that can point to a unique object (here, aperson
object). This explains whybaz(Person1)
modifiesPerson1.age
andbar1.foo.age
to 27, sincePerson1
andbar1.foo
are just two names for the sameperson
object (Person1 is bar1.foo
returnsTrue
, in Python).2) The second important notion is that of assignments. In
variable
arg1
is local, so that the codefirst does
arg1 = meh
, which means thatarg1
is an additional (local) name for listmeh
; but doingarg1 = [3, 2, 1]
is like saying "I changed my mind:arg1
will from now on be the name of a new list, [3, 2, 1]". The important thing to keep in mind here is that assignments, despite being denoted with an "equal" sign, are asymmetrical: they give to a (mutable) object on the right-and-side an additional name, given in the left-hand side (that's why you can't do[3, 2, 1] = arg1
, as the left-hand side must be a name [or names]). So,arg1 = meh; arg1 = [3, 2, 1]
cannot changemeh
.3) The last point is related to the question title: "passing by value" and "passing by reference" are not concepts that are relevant in Python. The relevant concepts are instead "mutable object" and "immutable object". Lists are mutable, while numbers are not, which explains what you observe. Also, your
Person1
andbar1
objects are mutable (that's why you can change the person's age). You can find more information about these notions in a text tutorial and a video tutorial. Wikipedia also has some (more technical) information. An example illustrates the difference of behavior between mutable and immutable:The last line is not equivalent to
y = y + [1, 2, 3]
because this would only put a new list object in variabley
instead of changing the list referred to by bothy
andx
.The three concepts above (variables as names [for mutable objects], asymmetrical assignment, and mutability/immutability) explain many of Python's behaviors.
不,实际上,它不会:
这是覆盖变量和修改变量指向的实例之间的区别。
列表、字典、集合和对象是可变类型。如果您在其中的实例中添加、删除、设置、获取或以其他方式修改某些内容,则会更新引用该实例的所有内容。
但是,如果您将类型的全新实例分配给变量,则会更改存储在该变量中的引用,因此旧的引用实例不会更改。
No, actually, it won't:
It's the difference between overwriting a variable and modifying the instance pointed to by a variable.
Lists, dictionaries, sets, and objects are mutable types. If you add, remove, set, get, or otherwise modify something in an instance of them, it updates everything that references that instance.
If, however, you assign a completely new instance of the type to a variable, that changes the reference stored in that variable, and thus the old referenced instance is not changed.
Python 没有按值传递,也没有按引用传递,而是使用按对象传递——换句话说,对象直接传递到函数中,并绑定到函数定义中给出的参数名称。
当您执行 spam = "green" 时,您已将名称 spam 绑定到字符串对象“green”;如果您随后执行“eggs = spam”,则您没有复制任何内容,也没有创建引用指针;您只需将另一个名称“eggs”绑定到同一个对象(在本例中为“green”)。如果您随后将垃圾邮件绑定到其他内容(垃圾邮件 = 3.14159),鸡蛋仍将绑定到“绿色”。
在您的 teh 函数中,您不会更改/修改/改变传入的对象,而是将名称 arg1 重新分配给不同的列表。要更改
arg1
你需要这样:Python does not have pass-by-value, nor pass-by-reference, but instead uses pass-by-object -- in other words, objects are passed directly into functions, and bound to the parameter name given in the function definition.
When you do spam = "green", you have bound the name spam to the string object "green"; if you then do eggs = spam you have not copied anything, you have not made reference pointers; you have simply bound another name, eggs, to the same object ("green" in this case). If you then bind spam to something else (spam = 3.14159) eggs will still be bound to "green".
In your
teh
function you are not changing/modifying/mutating the passed in object, you are reassigning the namearg1
to a different list. To changearg1
you want this: