
同一份数据,传给两个函数,一个改了外面,一个没改。
def f(lst):
lst.append(1)
def g(lst):
lst = [1]
data = []
f(data)
print(data) # [1]
data = []
g(data)
print(data) # []
都是 lst,为什么一个生效一个不生效?
Python 传的是对象引用,但赋值只是换个名字。
01
先看 id 怎么变
传参时,函数里的形参和外面的实参指向同一个对象。所以在函数里做原地修改,外面能看到。
def f(lst):
print(id(lst))
data = []
f(data)
print(id(data)) # 两个 id 一样
而 lst = [1] 是重新绑定:它让 lst 这个名字指向一个新对象,原来那个对象没动。外面的 data 还指着旧的。
def g(lst):
lst = [1]
print(id(lst)) # 新的 id
data = []
g(data)
print(id(data)) # 还是旧的
02
可变和不可变,表现不一样
列表、字典、set 这类可变对象,原地修改会穿透到外部,看起来像「引用传递」。
整数、字符串、元组这类不可变对象,任何「修改」都会新建对象、重新绑定,看起来又像「值传递」。
def add_one(n):
n += 1
x = 10
add_one(x)
print(x) # 10,没变
所以「值传递还是引用传递」这个问题本身问错了。准确说法是:传递对象引用,赋值改变绑定。
03
实际写代码时的三条建议
第一,函数默认不要原地修改传进来的参数,除非函数名明确说了会改,比如 sort()。
第二,需要修改就返回新对象,让调用方自己决定要不要接。sorted() 就是这么设计的。
第三,参数默认值别放可变对象,这就是 A01 那个坑的同一个根源。
|
函数里的操作 |
外部是否受影响 |
原因 |
|
lst.append(x) |
是 |
原地修改同一个对象 |
|
lst[0] = x |
是 |
原地修改 |
|
lst = [x] |
否 |
重新绑定,新对象 |
|
n += 1(int) |
否 |
不可变,必然新建 |
|
d.update(...) |
是 |
原地修改 |
传的是引用,改的是对象。
赋值只改名字,不改别人。
搞清楚这一层,很多「为什么函数改了我的数据」的疑问会一起消失。
扫码二维码 获取免费视频学习资料

- 本文固定链接: http://www.phpxs.com/post/14693/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料