Q1次のコードを実行したときの x の値はどれですか?def f(value):
value += 10
x = 5
f(x)
print(x)
関数の引数と参照渡し — ミュータブルだと引数が変更される
Python の関数の引数と参照渡しを図解で解説します。list と int で挙動が違う仕組み、.copy() で引数を守るパターンまで一通り押さえます。
関数を呼び出す際、引数がミュータブル型(list / dict / set)かイミュータブル型(int / str / tuple)かで挙動が異なります。この記事では、その違いと安全に書くための基本パターンを見ていきましょう。
イミュータブル型の引数 — 関数の中の変更は外に出ない
整数・浮動小数点・文字列・タプルはイミュータブル(変更不可能)です。これらを関数の引数として受け取り、中で += 10 や = 別の値 のように書き換えても、呼び出し元の変数は元の値のままです。
関数の中で += 10 をすると、新しい値が作られて関数内の引数名だけが付け替わるため、呼び出し元の名札は元の値を指したまま残る、というのが仕組みです。
def try_modify_number(value):
value += 10
print(f"関数内: {value}")
x = 5
try_modify_number(x)
print(f"関数外: {x}")
# 関数内: 15
# 関数外: 5 ← 元のまま
# 文字列・タプルでも同じ
def try_modify_text(text):
text = text + " world"
print(f"関数内: {text}")
message = "hello"
try_modify_text(message)
print(f"関数外: {message}") # hello ← 元のまま
ミュータブル型の引数 — 関数の中の変更が外にも見える
一方で、list / dict / set のようなミュータブル型を引数に渡すと、関数の中で .append() / .update() などで中身を直接書き換えると、呼び出し元の変数にも同じ変更が反映されます。
関数に渡された時点で、呼び出し元の名札と関数内の引数名は同じ箱を共有しているからです。以前の「ミュータブルとイミュータブル」で見た y = x で同じ箱を指すのと同じ仕組みが、関数呼び出しでも起きています。
def try_modify_list(items):
items.append(100)
print(f"関数内: {items}")
my_list = [1, 2, 3]
try_modify_list(my_list)
print(f"関数外: {my_list}")
# 関数内: [1, 2, 3, 100]
# 関数外: [1, 2, 3, 100] ← 関数外にも増えている
# 辞書でも同じ
def set_role(user, role):
user["role"] = role
admin = {"name": "田中"}
set_role(admin, "admin")
print(admin) # {'name': '田中', 'role': 'admin'}
関数内の変更が外にまで漏れると、原因が追いにくい
cart.append(...) はその行だけを見ると意図どおりに見えます。しかし関数内で書いた append が外の my_list にも伝わるため、「いつの間にかリストの中身が増えていた」という現象が、別の場所で発覚することがあります。
関数の中だけで変更したい — .copy() で引数を守る
呼び出し元のデータは残したまま、関数の中でだけ変更したいときは、関数の先頭で .copy() を挟めばよいです。受け取ったリスト/辞書/集合を別の箱にコピーしてから書き換えれば、呼び出し元には影響ありません。
変更した結果は return で返すので、呼び出し側は 元のカート と 新しいカート を両方持てます。このパターンを身につけると、副作用(呼び出し元の書き換え)のない安全な関数を書けます。
全体の流れ:①引数を .copy() で別の箱にコピー → ②コピー側を編集 → ③ return で結果を返す。この 3 ステップを覚えておけば、ミュータブル型を引数に取る関数を安全に設計できます。
def add_item_safely(cart, item):
items = cart.copy() # 別の箱にコピーしてから編集
items.append(item)
return items # 結果は return で返す
my_cart = ["牛乳", "パン"]
new_cart = add_item_safely(my_cart, "卵")
print(my_cart) # ['牛乳', 'パン'] ← 影響なし
print(new_cart) # ['牛乳', 'パン', '卵'] ← 別物
理解度チェック
まずは1問ずつ答えてみましょう。
Q2次のコードを実行したときの my_list の値はどれですか?def g(items):
items.append(100)
my_list = [1, 2, 3]
g(my_list)
print(my_list)
Q3呼び出し元のリストを書き換えずに、新しいリストを返す関数を書くときに、最初にすべき処理はどれですか?