per*_*lis 3 python oop reference pass-by-reference
在更改对象时以及何时不在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
Run Code Online (Sandbox Code Playgroud)
输出是
23
23
27
27
[1, 2, 3]
Run Code Online (Sandbox Code Playgroud)
因此,当我们声明Person1时,Person1.age为21.对此对象的引用将传递给另一个bar实例的类构造函数,名为bar1.对此引用所做的任何更改都将更改Person1.
当我们将Person1传递给普通函数时,Person1.age现在等于27.
但为什么这不适用于变量"meh"?当然,如果我们分配一个变量a = meh并进行更改a = [6, 6, 6],那么meh也会被更改.我糊涂了.有没有关于这一切是如何工作的文献?
我可以看到三个基本的Python概念可以对这个问题有所启发:
1)首先,从像一个可变对象的赋值
self.foo = arg1
Run Code Online (Sandbox Code Playgroud)
就像复制指针(而不是指向的值):self.foo并且arg1是同一个对象.这就是为什么接下来的那条线,
self.foo.age = 23
Run Code Online (Sandbox Code Playgroud)
修改arg1(即Person1).  因此,变量是可以指向唯一对象(这里是person对象)的不同"名称"或"标签".这解释了为什么baz(Person1)修改Person1.age 和 bar1.foo.age 27,因为Person1并且bar1.foo只是同一个 person对象的两个名称(在Python中Person1 is bar1.foo返回True).
2)第二个重要的概念是作业.在
def teh(arg1):
    arg1 = [3,2,1]
Run Code Online (Sandbox Code Playgroud)
变量arg1是本地的,所以代码
meh = [1,2,3]
teh(meh)
Run Code Online (Sandbox Code Playgroud)
首先arg1 = meh,这意味着这arg1是列表的附加(本地)名称meh; 但这样做arg1 = [3, 2, 1]就像是说"我改变了主意:arg1从现在起将是新名单的名称,[3,2,1]".这里要记住的重要一点是,尽管用"相等"的符号表示,但是赋值是不对称的:它们在右侧和右侧给出一个(可变的)对象,在左侧给出另一个名称.一面(这就是为什么你不能这样做[3, 2, 1] = arg1,因为左手边必须是一个名字[或名字]).所以,arg1 = meh; arg1 = [3, 2, 1]不能改变meh.
3)最后一点与问题标题有关:"按值传递"和"按引用传递"不是Python中相关的概念.相关的概念是" 可变对象 "和" 不可变对象 ".列表是可变的,而数字不是,这解释了你观察到的.此外,您Person1和bar1对象是可变的(这就是为什么您可以改变人的年龄).您可以在文本教程和视频教程中找到有关这些概念的更多信息.维基百科也有一些(更多技术)信息.一个例子说明了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
Run Code Online (Sandbox Code Playgroud)
最后一行是不等同于y = y + [1, 2, 3],因为这只会把一个新的列表对象变量y,而不是改变由双方提到的名单y和x.
上面的三个概念(变量作为名称[用于可变对象],不对称赋值和可变性/不变性)解释了许多Python的行为.
当然,如果我们分配变量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]
Run Code Online (Sandbox Code Playgroud)
这是覆盖变量和修改变量指向的实例之间的区别.
列表,词典,集合和对象是可变类型.如果添加,删除,设置,获取或以其他方式修改的东西在他们的一个实例,它更新引用该实例的一切.
但是,如果将一个全新的类型实例分配给变量,则会更改存储在该变量中的引用,因此不会更改旧引用的实例.
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'
Run Code Online (Sandbox Code Playgroud)