python函数中参数的传递机制疑惑理解

代码1

1
2
3
4
5
6
def foo(arg):
arg += 1

a = 2
foo(a)
print(a)

2

代码2

1
2
3
4
5
6
def foo(arg):
arg.append(6)

a = [1, 2, 3]
foo(a)
print(a)

[1, 2, 3, 6]

可以看出,
在执行完 foo 函数后
代码1 变量 a 值没有变化,还是 2
代码2 变量 a 值发生了变化,变为[1, 2, 3, 6]

为什么会这样呢?
还是重点Python中变量叫“名称”,赋值叫“贴标签”

结合python中一切皆对象(object)的特点
a = 2, 实际是将整数对象 2 贴上了 a 这个标签
a = [1, 2, 3] ,实际是将列表对象 [1, 2, 3] 贴上了 a 这个标签

现在再来分析这两个代码
代码1:
一开始,a=2 将整数对象2贴上标签a
接着执行 foo 函数,将整数对象2贴上标签arg
此时标签a、arg均指向整数对象2
函数执行 arg += 1 后
arg标签指向整数对象 3
而 a 标签还是指向整数对象2
这时候再执行 print(a),当然输出为 2

代码2:
当 a = [1, 2, 3] 时,标签 a 指向列表对象[1, 2, 3]
把 a 传递给 foo 函数时,arg 标签同样指向列表对象[1, 2, 3]
执行 append(6) 时,标签 a 和 arg 同时指向列表对象[1, 2, 3, 6]
所以最后打印结果为 [1, 2, 3, 6]

原因分析:
整数型 和 列表 的数据类型不同,
整数型对象是不可变类型,而列表是可变类型
当整数型 arg += 1 时,要保持原 a = 2 内存地址不变,系统会开辟一个新的空间来创造新的内存地址给引用arg
而列表对象在 append 时,列表天生就是可变的,所以不会创建新的内存地址
注意仅仅限于 append 方法, 如果用 += 方法也是会创建新的空间的
列表在使用 append方法进行改变时只是在当前做改变,所以代码中标签 a、arg 它们均指向了同一个内存地址

总结
Python中的–赋值传递,它的关键之处在于数据类型是否可变
整数、元组、字符串是不可变的,所以它们作为参数进行函数操作时,会创建新的内存地址以供引用
列表、字典是可变的,作为参数进行函数操作时,不会创建新的地址

所以,当不可变类型变量希望通过函数进行“数据改变”时,
代码1可以如此这般,重新贴一次标签

1
2
3
4
5
6
def foo(arg):
return arg + 1

a = 2
a = foo(a) # 重新贴一次标签
print(a)

3

注意:可变类型进行 += 操作时候,也是会创建新的内存空间的

1
2
3
4
5
6
def foo(arg):
return arg + [6]

a = [1, 2, 3]
foo(a)
print(a)

[1, 2, 3]

1
2
3
4
5
6
def foo(arg):
return arg + [6]

a = [1, 2, 3]
a = foo(a)
print(a)

[1, 2, 3, 6]

进一步结论,变量在进行函数操作后,值是否改变与这些有关
其一是:
变量类型是不是可变的
可变:列表、字典
不可变:整型、元组、字符串
其二是:
方法不同
+= 方法,不变,需要新开辟空间
append 方法,可变,不用新开空间,原基础上改变