ミュータブルとは、オブジェクトの識別子(メモリアドレス)を変更せずにオブジェクトを変更できるかどうかを定義します:
関数への影響:
例:
def f(lst): lst.append(42) data = [] f(data) print(data) # [42] def f2(x): x += 1 n = 1 f2(n) print(n) # 1
「次のコードは何を出力しますか?」
def foo(bar=[]): bar.append(1) return bar print(foo()) print(foo())
答え: 出力は:
[1]
[1, 1]
関数の引数は定義時に一度だけ初期化され、各呼び出しごとではありません。リスト(および他のミュータブルオブジェクト)は、関数の引数における一般的な落とし穴です。
物語
REST APIがデフォルト引数を持つ関数を介してリストを返していました:
def get_default_items(items=[]): items.append('x') return items
数回の呼び出しの後、リストが増えていることに気付き、1つの要素しか得られないと期待していました。
物語
関数内で文字列を「置き換える」予定でした:
def replace_word(word): word.replace('a', 'b') word = 'data' replace_word(word) print(word) # 'dbtb' を予想して 'data' を得た
strメソッドは元の文字列を変更せず、新しいものを返しますが、その返り値を無視しました。
物語
ネストされた構造で作業している際に:
original = [[1, 2], [3, 4]] copy = original[:] copy[0][0] = -1 print(original) # [[-1, 2], [3, 4]]
浅いコピーを使用し、コピーのみを変更したと考えていましたが、ネストされたオブジェクトは共有されていました。