Python 函数参数传递方式并不像其他语言那样是 指针/引用 和 值传递, 而是 对象引用传递,
本质上是 “引用传递”, 可以说 python 中不存在 值传递,而是存在 “不可变对象”
容易误判的操作
容易被误认为是 引用传递 的操作:
def case1(l: List[int]) -> None:
l.append(233)
alist = list()
case1(alist)
print(alist) # 输出: [233], 列表在函数内被改变了, 很像是 “引用/指针传递” 的效果
容易被误认为是 值传递 的操作:
def case2(s: str) -> None:
s += "dola"
print(s)
astring = ""
case2(astring)
print(s) # 输出仍然是 空字符串,但函数内的 输出为 “dola”, 很像是 “值传递” 的效果
对象引用传递
上面的两个例子中,实际本质都是其他语言中的 “引用传递”。
第一个例子比较好理解,alist 是一个对象的引用,与函数内的l, 指向的是同一个对象。
第二个例子 astring 其实也是对象的引用,不一样的是,这个对象是 不可变对象,第一个例子的对象是可变对象。
python 规定了 str 是 不可变对象
,对不可变对象进行操作,都需要重新拷贝一份。
也就是说,函数 def case2(s: str) 中, 一开始, s 和 函数外的 alist 指向的是同一个对象,当执行
s += "dola" 时,拷贝了一个新的对象进行操作,因此在此操作后,s 和 函数外的 alist 指向的是不同的对象了。
效果上,就像是其他语言的值传递。
可变对象 和 不可变对象
在 Python中,对 数据类型进行了一层封装, 所有数据类型都是对象,分为两类:
可变对象:list, dict, set
不变对象:bool, int, float, tuple, str, frozenset
因此,数据对象的操作,解释器会根据其对象类型进行不同的操作。
对于 可变对象:会直接操作引用所指向对象
对于 不可变对象: 会先拷贝出一个新的对象,然后对新的对象做相关操作。
默认参数只计算 1 次
def test(l=[1]):
l.append(1)
print(l)
test() # [1, 1]
test() # [1, 1, 1]
可变参数 *args, **kwargs
args 会被打包成 tuple
kwargs 会被打包成 dict