函數(shù)的參數(shù)作為引用
Python 唯一支持的參數(shù)傳遞是 共享傳參 ,也就是常說(shuō)的引用傳參。
函數(shù)內(nèi)部的形參是實(shí)參的別名
def f(a, b):
a += b
return a
# 數(shù)字,不變
x, y = 1, 2
f(x, y)
print(x, y)
# 列表可變類型,變了
x, y = [1, 2], [3, 4]
f(x, y)
print(x, y)
# 元組不可變類型,不變
x, y = (1, 2), (3, 4)
f(x, y)
print(x, y)
1 2
[1, 2, 3, 4] [3, 4]
(1, 2) (3, 4)
函數(shù)參數(shù)的默認(rèn)值
避免使用可變類型作為參數(shù)的默認(rèn)值
class HauntedBus:
def __init__(self, passengers=[]):
self.passengers = passengers
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)
# 給定參數(shù)沒有問題
bus1 = HauntedBus(['Alice', 'Bob'])
print(bus1.passengers)
bus1.pick('Canny')
bus1.drop('Alice')
bus1.passengers
['Alice', 'Bob']
['Bob', 'Canny']
# 使用參數(shù)的默認(rèn)值,即 []
bus2 = HauntedBus()
bus2.pick('David')
bus2.passengers
['David']
# 同樣使用默認(rèn)值,問題就來(lái)了
bus3 = HauntedBus()
bus3.passengers
['David']
bus3.pick('Bruce')
bus2.passengers
['David', 'Bruce']
bus3.passengers is bus2.passengers
True
HauntedBus.__init__.__defaults__
(['David', 'Bruce'],)
HauntedBus.__init__.__defaults__[0] is bus2.passengers
True
bus3 按道理使用的是默認(rèn)值 [],但是得到的確實(shí) bus2 的 passengers 值,而且它們都是同一個(gè)引用。
默認(rèn)值在定義函數(shù)時(shí)計(jì)算,因此默認(rèn)值變成了函數(shù)對(duì)象的屬性。
如果默認(rèn)值是可變對(duì)象,而且修改了它的值,那么后續(xù)的函數(shù)調(diào)用都會(huì)受到影響。
__init__ 方法應(yīng)該這樣處理默認(rèn)值,防御可變參數(shù)
class XXX:
def __init__(self, passengers=None):
if passengers is None:
self.passengers = []
else:
self.passengers = list(passengers) # 注意不是直接賦值,而是副本賦值,關(guān)于是深淺拷貝視情況而定
...
除非確實(shí)是想修改通過參數(shù)傳入的對(duì)象,否則在類中直接把參數(shù)賦值給實(shí)例變量之前一定要想清楚,因?yàn)檫@樣會(huì)為參數(shù)對(duì)象創(chuàng)建別名。