Python関数入門:スコープとライフタイムの基礎

本記事では、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)があります。

  1. Local:関数内
  2. Enclosing:入れ子になった関数の外側
  3. Global:モジュールレベル
  4. 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関数の基礎:スコープとライフタイム」に関する解説と演習問題です。

演習を通じて理解を深め、ぜひ実際のコードで試してみてください!