本記事では、Pythonの関数を初級者向けに解説し、特に「スコープ(有効範囲)」と「ライフタイム(変数の寿命)」という重要な概念にフォーカスします。
関数を正しく使いこなすためには、この2つの概念をしっかり理解することが欠かせません。
記事後半には演習問題とその解答例も用意していますので、実際に手を動かしながら学習を進めましょう。
スコープ(有効範囲)の理解
グローバルスコープとローカルスコープ
グローバルスコープは、モジュール(ファイル)全体で有効な変数です。
ローカルスコープは、関数内部でのみ有効な変数です。
x = 10 # グローバル変数
def func():
y = 5 # ローカル変数
print(x) # グローバル変数は読める
# print(z) # NameError: zは定義されていない
func()
print(y) # NameError: yは関数外で見えない
LEGBルール
Pythonでは変数を探す順番として「Local → Enclosing → Global → Built-in」の4段階(LEGB)があります。
- Local:関数内
- Enclosing:入れ子になった関数の外側
- Global:モジュールレベル
- Built-in:Python組み込み名前空間
global と nonlocal
globalは、関数内からグローバル変数を書き換えたいときに宣言します。
nonlocalは、入れ子関数で、外側(しかしグローバルではない)変数を操作するときに宣言します。
count = 0
def increment():
global count
count += 1
increment()
print(count) # → 1
ライフタイム(寿命)の理解
変数の生成から破棄まで
関数を呼び出すときにローカル変数は作られ、処理終了後に破棄されます。
グローバル変数はプログラム終了まで生存します。
def demo():
a = [1, 2, 3]
print(a)
demo()
# demo() 実行後、aはメモリから解放される(参照がなくなればGCの対象)
メモリ管理と参照カウント
Pythonは参照カウント+ガベージコレクションでメモリを管理します。
関数内で作ったオブジェクトは、関数終了時に参照がなくなるため、自動的に回収されます。
具体例で確認
message = "グローバル"
def outer():
message = "外側"
def inner():
nonlocal message
message = "内側"
print("inner:", message) # → 内側
inner()
print("outer:", message) # → 内側
outer()
print("global:", message) # → グローバル
inner関数内でnonlocal messageを宣言しているため、outerのmessageを書き換えていて、最後のprintでグローバルのmessageは変化しません。
まとめ
- スコープ:変数が見える範囲
- ライフタイム:変数が存在する期間
- 関数を正しく設計するためには、これらを意識して変数の定義・更新を行う
演習問題
問題1
以下のコードを実行したときの出力を予想してください。
x = 1
def f():
x = 2
def g():
print(x)
g()
f()
print(x)
問題2
次のコードにglobalやnonlocalを加えて、inner関数内からグローバル変数cntと外側関数outerの変数cntを両方ともインクリメントできるように修正してください。
cnt = 0
def outer():
cnt = 10
def inner():
# ここでグローバルcntを +1
# ここでouterのcntを +1
pass
inner()
print("outer cnt:", cnt)
outer()
print("global cnt:", cnt)
問題3
関数を使って、呼び出されるたびに合計値を保持し続ける(累積する)仕組みを実装してください。
グローバル変数、クロージャー、クラスのいずれか1つを利用した方法をそれぞれ1つずつ示してください。
演習問題の解答例
解答1
2
1
- f()内でx = 2を定義し、そのxをg()が参照 → 2
- グローバルのxは変更されず → 1
解答2
cnt = 0
def outer():
global cnt
cnt = 10 # グローバルcntを上書き(必要に応じて初期化)
def inner():
global cnt # グローバルcntを操作
cnt += 1
nonlocal cnt_outer # outerのcntを操作するためのエイリアス
cnt_outer += 1
cnt_outer = cnt # outer用のローカル変数に一時保存
inner()
print("outer cnt:", cnt_outer)
outer()
print("global cnt:", cnt)
※ポイント:nonlocalは関数定義直前にある変数しか指定できないため、cnt_outerという中間変数を使ってouterの変数を参照。
解答3
1) グローバル変数を利用
total = 0
def add_global(n):
global total
total += n
return total
2) クロージャーを利用
def make_accumulator():
total = 0
def add(n):
nonlocal total
total += n
return total
return add
acc = make_accumulator()
3) クラスを利用
class Accumulator:
def __init__(self):
self.total = 0
def add(self, n):
self.total += n
return self.total
acc_obj = Accumulator()
以上が「Python関数の基礎:スコープとライフタイム」に関する解説と演習問題です。
演習を通じて理解を深め、ぜひ実際のコードで試してみてください!