难以理解 Python 中传递值和引用

发布于 2024-12-11 04:31:17 字数 773 浏览 0 评论 0原文

遇到对象何时更改以及何时不在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

尐籹人 2024-12-18 04:31:17

我可以看到三个基本的 Python 概念可以阐明这个问题:

1)首先,从像 in 这样的可变对象进行赋值

self.foo = arg1

就像复制指针(而不是指向的值):self.foo 和 arg1 是同一个对象。这就是接下来的行

self.foo.age = 23

修改 arg1 (即 Person1)的原因。 变量因此是可以指向唯一对象的不同“名称”或“标签”(此处为person对象)。这解释了为什么 baz(Person1)Person1.age bar1.foo.age 修改为 27,因为 < code>Person1 和 bar1.foo 只是相同 person 对象的两个名称(Person1 是 bar1.foo 返回True,在 Python 中)。

2)第二个重要的概念是作业。变量

def teh(arg1):
    arg1 = [3,2,1]

arg1 是本地变量,因此代码

meh = [1,2,3]
teh(meh)

首先执行 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中相关的概念。相关概念是“可变对象”和“不可变对象”。列表是可变的,而数字则不然,这解释了您所观察到的情况。此外,您的 Person1bar1 对象是可变的(这就是您可以更改此人的年龄的原因)。您可以在文本教程视频教程。维基百科还有一些(更多技术性)信息。一个示例说明了可变和不可变之间的行为差​​异:

x = (4, 2)
y = x  # This is equivalent to copying the value (4, 2), because tuples are immutable
y += (1, 2, 3)  # This does not change x, because tuples are immutable; this does y = (4, 2) + (1, 2, 3)

x = [4, 2]
y = x  # This is equivalent to copying a pointer to the [4, 2] list
y += [1, 2, 3]  # This also changes x, because x and y are different names for the same (mutable) object

最后一行相当于y = y + [1, 2, 3],因为这只会放置一个变量y中的新列表对象,而不是更改yx引用的列表。

上面的三个概念(变量作为名称[用于可变对象]、不对称赋值和可变性/不可变性)解释了 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

self.foo = arg1

is like copying a pointer (and not the value pointed to): self.foo and arg1 are the same object. That's why the line that follows,

self.foo.age = 23

modifies arg1 (i.e. Person1). Variables are thus different "names" or "labels" that can point to a unique object (here, a person object). This explains why baz(Person1) modifies Person1.age and bar1.foo.age to 27, since Person1 and bar1.foo are just two names for the same person object (Person1 is bar1.foo returns True, in Python).

2) The second important notion is that of assignments. In

def teh(arg1):
    arg1 = [3,2,1]

variable arg1 is local, so that the code

meh = [1,2,3]
teh(meh)

first does arg1 = meh, which means that arg1 is an additional (local) name for list meh; but doing arg1 = [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 change meh.

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 and bar1 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:

x = (4, 2)
y = x  # This is equivalent to copying the value (4, 2), because tuples are immutable
y += (1, 2, 3)  # This does not change x, because tuples are immutable; this does y = (4, 2) + (1, 2, 3)

x = [4, 2]
y = x  # This is equivalent to copying a pointer to the [4, 2] list
y += [1, 2, 3]  # This also changes x, because x and y are different names for the same (mutable) object

The last line is not equivalent to y = y + [1, 2, 3] because this would only put a new list object in variable y instead of changing the list referred to by both y and x.

The three concepts above (variables as names [for mutable objects], asymmetrical assignment, and mutability/immutability) explain many of Python's behaviors.

谈下烟灰 2024-12-18 04:31:17

当然,如果我们赋值一个变量 a = meh 并改变 a = [6, 6, 6],那么 meh 也会改变。

不,实际上,它不会:

>>> meh = [1,2,3]
>>> a = meh
>>> a = [6, 6, 6]
>>> print a
[6, 6, 6]
>>> print meh
[1, 2, 3]

这是覆盖变量和修改变量指向的实例之间的区别。

列表、字典、集合和对象是可变类型。如果您在其中的实例中添加、删除、设置、获取或以其他方式修改某些内容,则会更新引用该实例的所有内容。

但是,如果您将类型的全新实例分配给变量,则会更改存储在该变量中的引用,因此旧的引用实例不会更改。


a = [1,2,3] # New instance
a[1] = 4    # Modifying existing instance

b = {'x':1, 'y':2} # New instance
b['x'] = 3         # Modifying existing instance

self.x = [1,2,3] # Modifying existing object instance pointed to by 'self',
                 # and creating new instance of a list to store in 'self.x'

self.x[0] = 5    # Modifying existing list instance pointed to by 'self.x'

Certainly, if we assign a variable a = meh and change a = [6, 6, 6], then meh will also be changed.

No, actually, it won't:

>>> meh = [1,2,3]
>>> a = meh
>>> a = [6, 6, 6]
>>> print a
[6, 6, 6]
>>> print meh
[1, 2, 3]

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.


a = [1,2,3] # New instance
a[1] = 4    # Modifying existing instance

b = {'x':1, 'y':2} # New instance
b['x'] = 3         # Modifying existing instance

self.x = [1,2,3] # Modifying existing object instance pointed to by 'self',
                 # and creating new instance of a list to store in 'self.x'

self.x[0] = 5    # Modifying existing list instance pointed to by 'self.x'
生活了然无味 2024-12-18 04:31:17

Python 没有按值传递,也没有按引用传递,而是使用按对象传递——换句话说,对象直接传递到函数中,并绑定到函数定义中给出的参数名称。

当您执行 spam = "green" 时,您已将名称 spam 绑定到字符串对象“green”;如果您随后执行“eggs = spam”,则您没有复制任何内容,也没有创建引用指针;您只需将另一个名称“eggs”绑定到同一个对象(在本例中为“green”)。如果您随后将垃圾邮件绑定到其他内容(垃圾邮件 = 3.14159),鸡蛋仍将绑定到“绿色”。

在您的 teh 函数中,您不会更改/修改/改变传入的对象,而是将名称 arg1 重新分配给不同的列表。要更改 arg1 你需要这样:

def teh(arg1):
    arg1[:] = [3, 2, 1]

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 name arg1 to a different list. To change arg1 you want this:

def teh(arg1):
    arg1[:] = [3, 2, 1]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文