在更改对象时以及何时不在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也会被更改。我糊涂了。有没有关于这一切是如何运作的文献?
答案 0 :(得分:8)
我可以看到三个基本的Python概念可以对这个问题有所启发:
1)首先,来自
中的可变对象的赋值self.foo = arg1
就像复制指针(而不是指向的值):self.foo
和arg1
是相同的对象。这就是为什么接下来的行,
self.foo.age = 23
修改arg1
(即Person1
)。 变量因此可以指向唯一对象的不同“名称”或“标签”(此处为person
对象)。这解释了为什么baz(Person1)
将Person1.age
和 bar1.foo.age
修改为27,因为Person1
和bar1.foo
只是相同的 person
对象(Person1 is bar1.foo
在Python中返回True
。
2)第二个重要的概念是作业。在
def teh(arg1):
arg1 = [3,2,1]
变量arg1
是本地的,因此代码
meh = [1,2,3]
teh(meh)
首先执行arg1 = meh
,这意味着arg1
是列表meh
的附加(本地)名称;但做arg1 = [3, 2, 1]
就像是说“我改变了主意:arg1
将从现在起成为 new 列表的名称,[3,2,1]”。这里要记住的重要一点是赋值,尽管用“相等”符号表示,但是不对称:它们在右侧和右侧给出一个(可变的)对象的附加名称在左侧给出(这就是为什么你不能做[3, 2, 1] = arg1
,因为左边必须是名字[或名字])。因此,arg1 = meh; arg1 = [3, 2, 1]
无法更改meh
。
3)最后一点与问题标题有关:“按值传递”和“按引用传递”不是Python中相关的概念。相关的概念是“可变对象”和“不可变对象”。列表是可变的,而数字不是,这解释了你观察到的。此外,您的Person1
和bar1
对象是可变的(这就是您可以更改此人年龄的原因)。您可以在text tutorial和video tutorial中找到有关这些概念的更多信息。维基百科也有一些(more technical) information。一个例子说明了mutable和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
最后一行不等同于y = y + [1, 2, 3]
,因为这只会在变量y
中放置一个新的列表对象,而不是更改{{1}引用的列表}和y
。
上面的三个概念(变量作为名称[用于可变对象],非对称赋值和可变性/不变性)解释了许多Python的行为。
答案 1 :(得分:6)
当然,如果我们分配一个变量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]
覆盖变量与修改变量指向的实例之间的区别。
列表,词典,集合和对象是可变类型。如果您添加,删除,设置,获取或以其他方式修改 in 中的某个实例,它会更新引用该实例的所有内容。
但是,如果您为变量分配了完全新实例,则会更改存储在该变量中的引用,因此不会更改旧引用的实例。
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'
答案 2 :(得分:0)
Python没有pass-by-value,也没有pass-by-reference,而是使用pass-by-object - 换句话说,对象直接传递给函数,并绑定到函数中给出的参数名称功能定义。
当您执行spam =“green”时,您已将垃圾邮件名称绑定到字符串对象“green”;如果你那么做鸡蛋=垃圾邮件,你没有复制任何东西,你没有做出参考指针;你只需将另一个名称egg,绑定到同一个对象(在这种情况下为“green”)。如果你然后将垃圾邮件绑定到其他东西(垃圾邮件= 3.14159),鸡蛋仍将被绑定为“绿色”。
在teh
函数中,您没有更改/修改/改变传入的对象,而是将名称arg1
重新分配给其他列表。要更改arg1
,您需要这样:
def teh(arg1):
arg1[:] = [3, 2, 1]