関数を呼んだら、渡したリストの中身まで変わっていた。この挙動を理解するには、代入、参照、関数の引数、可変性を一緒に考える必要があります。この単元ではPythonを例に、短いコードを実行順に追いながら、値がどこへ渡り、何が変更されるかを説明します。
値・型・式を区別する
値はプログラムが扱うデータで、型はその値に対してどんな操作ができるかを定めます。Pythonでは3は整数、"3"は文字列です。見た目が似ていても、足し算や比較の意味が変わります。式は評価されて値を生む表現です。3+4は値7を生みます。一方、代入文は名前とオブジェクトを結び付け、後続の計算で使えるようにします。
price = 120
count = 3
total = price * count
print(total) # 360
print("120" * count) # 120120120動的型付けは「型がない」という意味ではありません。Pythonではオブジェクトに型があり、実行時の値に応じて演算の可否が決まります。型注釈は人や検査ツールへ意図を伝えますが、注釈を書くだけで実行時にすべての値が自動検証されるわけではありません。入力境界では変換や検査を明示します。
制御構造は実行する順序を決める
順次実行、条件分岐、反復を使って、入力に応じた処理を組み立てます。ifでは条件が真の場合の枝を選び、forでは要素を順に取り出します。ループの確認では、最初の状態、一回の繰返しで変わる状態、終了条件を分けて追います。「だいたい合う」だけでなく、空の入力や境界の値でも同じ説明が成り立つかを確かめます。
def sum_positive(values):
total = 0
for value in values:
if value > 0:
total += value
return total
print(sum_positive([-2, 0, 3, 5])) # 8
print(sum_positive([])) # 0このループでは「ここまで見た正の値の合計がtotalに入っている」という性質が毎回保たれます。これを不変条件として意識すると、初期値が0でよい理由、負数を足さない理由、ループ終了時に答えが得られる理由を説明できます。returnはその時点で関数を終えるため、置く位置を変えると処理対象も変わります。
関数は入力・戻り値・副作用で読む
関数には、引数から戻り値を計算する役割と、外部の状態を変更する役割があります。ファイル出力やリストの変更も副作用です。戻り値が同じでも、呼出し元のデータを変える関数と変えない関数は使い方が異なります。名前や説明には、何を返すかだけでなく、渡した値を変更するかも示すと扱いやすくなります。
def add_item(items, value):
items.append(value)
a = [1]
b = a
add_item(a, 2)
print(a, b) # [1, 2] [1, 2]
b = [9] # bを別のリストへ結び直す
print(a, b) # [1, 2] [9]aとbは、最初は同じリストを参照しています。appendはそのリストを変更するため両方から変更が見えます。b=[9]はbの結び付きを変え、aが参照するリストの中身は変えません。「変数が同じ値を持つ」と「同じオブジェクトを参照する」を区別することが大切です。
スコープとモジュール
スコープは名前を参照できる範囲です。関数内で代入した名前は、通常その関数のローカルな名前になります。外側にも同名の変数がある場合は、どちらを使っているかを確認します。広い範囲の状態を無意識に書き換える関数は追跡しにくいため、必要な値を引数で渡し、結果を戻り値にする構成を基本にすると見通しがよくなります。
モジュールは関連する定義をまとめ、名前空間を分ける単位です。importしただけで何が実行されるかにも注意します。重い計算やファイルの変更をトップレベルへ置くと、読み込むだけで副作用が起きます。再利用する関数の定義と、コマンドとして実行したときの処理を分けると、テストや再利用が容易になります。
理解を確かめる
sum_positiveのreturnをforの内側へ移すとどうなるか。最初の要素を処理した段階で終了します。空の入力ではループへ入らず、末尾まで到達すればPythonはNoneを返します。
a=[1]、b=aの後でb.append(2)を実行するとaはどうなるか。同じリストを参照しているため[1, 2]になります。b=a.copy()なら別の外側リストです。
型注釈を付けた関数に不正な型を渡せば必ず入口で拒否されるか。Pythonの注釈自体にはその保証がありません。必要な検証は別に実装します。
この単元の概念:型 スコープ 可変性
続けて読む:メモリ・ポインタ・参照・ライフタイム