sec03 - Pythonのスコープ完全ガイド:変数の可視範囲とLEGBルールの理解
スポンサーリンク

1. スコープとは何か?:Python変数の可視範囲を理解する

スコープ(scope)とは、変数が「どこから見えるか」「どこで使えるか」を決めるルールのことです。Pythonでは、変数が定義された場所によって、参照できる範囲が変わります。

スコープを理解すると、意図しないエラーを防ぎ、きれいで安全なコードを書けるようになります。

x = 10  # グローバルスコープ(どこからでもアクセス可能)

def sample():
    y = 5  # ローカルスコープ(関数の中だけで有効)
    print(x)  # OK
    print(y)  # OK

sample()
print(x)  # OK
print(y)  # エラー(yは関数の外から見えない)

上の例では、xは「グローバル変数」であり、どこからでも参照できます。一方で、yは「関数内のローカル変数」で、関数の外では見えません。

2. スコープの4階層(LEGBルール)と探索順序

Pythonには、スコープの探索ルールが決まっています。これを「LEGBルール」と呼びます。

LEGBルール

  • L (Local):関数内などのローカルスコープ
  • E (Enclosing):入れ子関数の外側関数のスコープ
  • G (Global):モジュール全体で有効なスコープ
  • B (Built-in):Python組み込みのスコープ(len, printなど)
x = 'global'

def outer():
    x = 'enclosing'
    def inner():
        x = 'local'
        print(f'07行目のx = {x}')  # local
    inner()
    print(f'09行目のx = {x}')  # enclosing

outer()
print(f'12行目のx = {x}')  # global
07行目のx = local
09行目のx = enclosing
12行目のx = global

Pythonは、変数を探すときにこの順番(L → E → G → B)で探していきます。

スポンサーリンク

3. グローバル変数とローカル変数の違い

グローバル変数は、モジュール全体で使える変数です。一方、ローカル変数は関数の中だけで使える変数です。

count = 0  # グローバル変数

def add():
    count = 10  # ローカル変数(別物)
    print('関数内:', count)

add()
print('関数外:', count)
関数内: 10
関数外: 0

この場合、関数内で定義されたcountは、グローバル変数countとは別物です。関数外の値は変わりません。

関数の中にcount = 10のような変数を定義する行が存在すると、Pythonは関数の中に出てくるcountをローカル変数として扱い、グローバル変数のcountは参照できなくなります。ですので、ローカル変数countの初期化が行われる前にcountの値を参照しようとするとエラーになります。

count = 0  # グローバル変数

def add():
    print('関数内:', count)  # ローカル変数が定義する前に参照しようとするとエラーになる
    count = 10  # ローカル変数(別物)

add()
UnboundLocalError: cannot access local variable 'count' where it is not associated with a value
(UnboundLocalError: 値に関連付けられていない場所でローカル変数 『count』 にアクセスできません)

最初の例のように、グローバル変数と同じ名前の関数内の変数に対して、値を代入するなどの編集を行わなければ、グローバル変数の値を参照することはできます。

x = 10  # グローバル

def sample():
    print(x)  # 変数xに対して関数の中で編集を行う処理を行わなければ、参照することは可能

sample()

4. global文でグローバル変数を更新する方法

関数の中からグローバル変数を変更したい場合は、global文を使います。

count = 0

def add():
    global count
    count += 1
    print('関数内:', count)

add()
print('関数外:', count)
関数内: 1
関数外: 1

global countと宣言することで、関数内でもグローバル変数countを操作・参照できるようになります。

5. nonlocal文と入れ子関数による変数操作

入れ子関数(関数の中に関数がある構造)では、外側の関数の変数をnonlocalで操作できます。

def outer():
    x = 0
    def inner():
        nonlocal x
        x += 1
        print('inner:', x)
    inner()
    print('outer:', x)

outer()
inner: 1
outer: 1

ここでnonlocal xを使うことで、inner()の中からouter()の変数xを更新できます。

6. スコープと変数のライフタイム(生存期間)

スコープと「変数の生存期間(ライフタイム)」は密接に関係しています。ローカル変数は関数が実行されるたびに作られ、終了時に破棄されます。

def counter():
    x = 0
    x += 1
    print(x)

counter()
counter()
1
1

この例では、毎回xがリセットされます。関数をまたいで値を保持したい場合は、次のような構成を考える必要があります。

  • グローバル変数を使う
  • クラス・クロージャなどの仕組みを利用する
  • 返り値と引数を使って、値を受け渡す

7. スコープを意識したクリーンな関数設計の実践例

スコープを意識すると、グローバル変数に依存しない「クリーンなコード」を設計できます。次の例では、グローバル変数を使わずに状態を保持する方法を紹介します。

def create_counter():
    count = 0
    def add():
        nonlocal count
        count += 1
        return count
    return add

counter = create_counter()
print(counter())  # 1
print(counter())  # 2
print(counter())  # 3

このようにnonlocalとクロージャを使えば、グローバル変数に頼らずに状態を管理できます。スコープを理解することで、保守性の高い関数設計が可能になります。

クロージャーについては専用のレクチャーで学習します。

スポンサーリンク