Python

【Python】関数内の関数【内部関数とクロージャ】

この記事では、Pythonの関数内の関数である「内部関数」とそれを応用した「クロージャ」について解説します。

内部関数とは

Pythonでは、関数内に関数を定義することができます。

def outer_func():
    #  外側の関数の処理

    def inner_func():
        # 内側の関数の処理

    #  外側の関数の処理

このような関数のことを「内部関数」「関数のネスト」と呼びます。

内側の関数(inner_func)は、関数の外側から直接呼び出すことができないので、外側の関数内で呼び出したり、関数の参照を戻り値として返したりします。

def outer_func():

    def inner_func():
        pass

    # 外側の関数内で呼び出す
    inner_func()

    # 戻り値で内側の関数の参照を返す
    return inner_func

クロージャ(関数閉包)

クロージャ(closure)とは、外側の関数内で宣言された変数や受け取った引数を記憶した内側の関数のことを言います。クロージャを内包した関数を「エンクロージャ(enclosure)」と呼びます。

# エンクロージャ
def outer_func(arg):

    # 関数内で宣言した変数(ローカル変数)
    num = 1

    # クロージャ
    def inner_func():
        # outer_func関数の引数と変数を足した値を返す
        return arg + num

    # クロージャを返す
    return inner_func

# inner_funcを代入    
f = outer_func(10)
print(f())
>> 11

本来ならば、outer_func関数の引数argとローカル変数numは、関数を抜けた時点で消滅しますが、クロージャを生成したことにより、inner_func関数内に記憶されたので、演算し、結果を戻り値として返すことができました❗️

エンクロージャのローカル変数の値を書き換える

先ほどのコードでは、outer_func関数のローカル変数numをクロージャ内で書き換えるとエラーとなります。

def outer_func(arg):

    num = 1

    def inner_func():
        # outer_func関数のローカル変数を書き換えてみる
        num += 1
        return arg + num

    return inner_func
    
f = outer_func(10)
print(f())
>> UnboundLocalError: local variable 'num' referenced before assignment

グローバルを除く1つ外側のスコープで、先に束縛された変数の値を書き換えるには、nonlocal文を使って再束縛する必要があります。

def outer_func(arg):

    num = 1

    def inner_func():
        # numを再束縛
        nonlocal num
        # 再束縛したので書き換え可能
        num = 100
        return arg + num

    return inner_func
    
f = outer_func(10)
print(f())
>> 110

このコードを応用することで、呼び出すたびに更新されるような関数を定義することができます。

def outer_func():

    count = 0

    def inner_func():
        # 再束縛
        nonlocal count
        # 1ずつ増やす
        count += 1
        return count

    return inner_func
    
f = outer_func()
print(f())
>> 1
print(f())
>> 2
print(f())
>> 3

fに変数が記憶されているので、呼び出すたびにcountが更新されます。

最短3か月でエンジニア転職『DMM WEBCAMP COMMIT』