函數中可變參數的應用
背景
在函數或類定義中傳入的參數是可變參數,常見的是字典、列表、數組(ndarray),函數內容如果僅僅是引用該這些對象沒有什么大問題。但是如果涉及增、刪操作,將會發生非常詭異的事情。
下面以《流暢的Python》中定義的一個案例進行介紹:
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)
passengers = ['Alice', 'Bill', 'Carrie', "Dave"]
bus1 = HauntedBus(passengers)
bus1.pick("Charlie")
bus1.drop("Alice") # Alice下車
print(f"bus1.passengers={bus1.passengers}") #
print(f"passengers={passengers}")
"""
bus1.passengers=['Bill', 'Carrie', 'Dave', 'Charlie']
passengers=['Bill', 'Carrie', 'Dave', 'Charlie']
"""
類HauntedBus違反了涉及接口的最佳實踐,即“最少驚訝原則”。學生從校車中下車后,他的名字就從籃球隊消失了。
簡單解釋就是:實例的修改不能影響其它外部變量。
解決問題
通過python內置的數據結構可知,列表、字典是可變的數據對象.而且賦值是淺拷貝,所在在該模式下修改賦值后的變量也會影響源變量。針對上述問題解決方案就是將對應的變量進行copy。
class HauntedBus:
def __init__(self, passengers=[]):
self.passengers = passengers.copy()
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)
passengers = ['Alice', 'Bill', 'Carrie', "Dave"]
bus1 = HauntedBus(passengers)
bus1.pick("Charlie")
bus1.drop("Alice") # Alice下車
print(f"bus1.passengers={bus1.passengers}") #
print(f"passengers={passengers}")
"""
bus1.passengers=['Bill', 'Carrie', 'Dave', 'Charlie']
passengers=['Alice', 'Bill', 'Carrie', 'Dave']
"""
然而copy并不總是有效,如果當前這個數據對象非常大的情況下,copy將比較耗時,此時比較便捷的方式就是基于目標數據結構進行輸出數據重構。
這種情況在輸入的參數是字典時(且嵌套層級較多時),尤為明顯。
ndarray中類似的問題
import numpy as np
a = np.arange(10)
def func(a):
"""測試,將a的前2個值修改為0"""
a[:2] = 0
return True
print(f"before a={a}")
func(a)
print(f"after a={a}")
"""
before a=[0 1 2 3 4 5 6 7 8 9]
after a=[0 0 2 3 4 5 6 7 8 9]
"""
def func2(a):
"""測試,將a的前2個值修改為0"""
b=a.copy()
b[:2] = 0
print(f"b={b}")
return True
c=np.arange(10)
print(f"before c={c}")
func2(c)
print(f"after c={c}")
"""
before c=[0 1 2 3 4 5 6 7 8 9]
b=[0 0 2 3 4 5 6 7 8 9]
after c=[0 1 2 3 4 5 6 7 8 9]
"""
小結
- 輸入的參數如果是可變對象,若涉及對象修改問題需慎重考慮:
- 單通道串行:正常操作即可
- 多通道并行(存在多個對象同時或部分同時應用的情況):
- 若數據體量較小,可以采用深拷貝或淺拷貝的方式
- 若數據體量較大,且淺拷貝不足以解決問題時,但一般情況不建議深拷貝。通常基于目標數據格式進行重組。

浙公網安備 33010602011771號