📄 一句话概括
参数进函数,是【复印一份】还是【把原件递进去】?——值传递给复印件(外面不受影响),引用传递递原件(外面看得见改动);Python 的规则一句话:不可变对象等效值传递、可变对象进去就能被改。
📝 本节和前面几节的关系
4-4 的参数怎么【进】机器,是新手最容易懵的一课。回收 3-14 的【房号与复印件】(b = a 拷的是房号)与 3-9 的【字符串不可变】——本节把这两课在函数门口合流。
一、两条传法:复印件 vs 原件
| 传法 | 进函数的是什么 | 函数内改动对外面 |
|---|---|---|
| 值传递 | 值的复印件 | 看不见 |
| 引用传递 | 对象的地址(房号) | 看得见(直接改原件) |
C 语言默认值传递(想要【能改外面】得亲手传指针,回收 3-14);C++ 提供引用形参两样都行;Python/Java 没有这两张选单——统一传【对象的引用】,但效果因对象可不可变而分岔。
二、Python 的分岔现场
def try_change(n, lst):
n = n + 1 # int 不可变:这步造了个新值,函数内的 n 指向新值
lst.append(99) # list 可变:顺着房号直接改原件
x = 10
my_list = [1, 2]
try_change(x, my_list)
print(x, my_list) # 10 [1, 2, 99]
x 没变、list 变了——因为:
- 不可变对象(int、float、字符串、元组):函数内【改】它其实是另起新值,函数外的名字还指着旧值——外面看不见;
- 可变对象(列表、字典、集合):函数拿到的是真房号,append/改字段都是上门施工——外面看得见。
还有一个更隐蔽的坑:函数内对参数重新赋值(lst = [9])只是把函数内的名字改指新房,不影响外面——【改房子】看得见,【换房号】看不见。
三、默认参数的大坑:可变默认值
def add_item(item, bag=[]): # 危险!默认列表只创建一次
bag.append(item)
return bag
print(add_item("a")) # ["a"]
print(add_item("b")) # ["a", "b"] —— 不是 ["b"]!
默认参数在函数定义时创建一次、之后所有调用共享同一个列表——这是 Python 面试第一题。正解:默认用 None,函数内再新建。
四、动手 Demo:三幕现场
def demo(n, lst):
n = 100
lst.append(100)
lst = [7] # 换房号:指向新列表
lst.append(8)
x = 1
arr = [1]
demo(x, arr)
print(x, arr) # 1 [1, 100]
看到什么算成功:输出 1 [1, 100]。
读结果:x 没变(int 不可变);arr 变了但只多了一个 100——append 生效(改原件),随后 lst = [7] 换房号、append(8) 都打在新房上,外面看不见。三行代码浓缩了本节全部要点。
五、30 秒实验
跑 def f(s): s = s + "x"\nt = "a"; f(t); print(t)——输出 a。字符串拼接造的是新字符串(不可变),函数内 s 的新指向与外面无关。再把参数换成列表试试 append——对比 30 秒,两条规则刻进脑子。
六、常见误区
- 【Python 一律值传递】或【一律引用传递】——都不对。它统一传引用,但不可变对象改不动原件(等效值传递)、可变对象改得动——按【可变/不可变】分岔,不按【传法】分岔。
- 【函数内赋值能影响外面的变量】——错。函数内对参数重新赋值只是换函数内那个名字的指向;要改外面得改对象本身(append、改字段)。
- 【可变默认参数很方便】——炸弹。默认对象全体调用共享,状态会【串场】;用 None 占位、函数内新建才是正解。
自测三问(点开对答案)
1. **为什么 Python 函数内改 int 不影响外面、改列表影响?** 答:int 不可变,函数内改是另起新值(外面名字仍指旧值);列表可变,函数持真房号、append 直接改原件。 2. **函数内 lst = [7] 后再 append,外面看得见吗?** 答:看不见——赋值是【换房号】(函数内名字指向新对象),只有【改对象本身】(append/改字段)才影响外面。 3. **可变默认参数为什么危险?怎么解?** 答:默认对象在定义时创建一次、所有调用共享,状态跨调用串场;正解是默认 None、函数内新建。- ☐ 下一节(4-6 作用域、全局变量、局部变量)
相关笔记
- 地址、指针 —— 房号与复印件的前置课
- C++ 6.3.1 · 传值调用 —— C 的复印件世界
- 字符编码与字符串 —— 不可变的出处
- ⌨️ 编程原理 · 索引 —— 全课路线图