Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
66
Help us understand the problem. What is going on with this article?
@naomi7325

【Python】クロージャ(関数閉方)とは

More than 1 year has passed since last update.

クロージャの説明

クロージャ(関数閉方)とは外側の変数を記憶した関数です。

# 例1
def func():
    x = 3
    def add3(y):
        return y+x
    return add3

f = func()
print(f(4)) # 7

func 関数は add3 関数を定義してそれを返します。
add3 関数は外側で定義された x を関数内で使用しています。
add3 関数をクロージャ、func 関数をエンクロージャといいます。
x は本来であれば func関数を抜けたところで消滅するfunc 関数におけるローカル変数です。
クロージャを生成したことで x が add3 関数内に記憶されて f(4) で呼び出した時に x が加算されます。

# 例2
def func(name):
    def add_msg(message):
        return "Hi " + name + ":" + message
    return add_msg

f = func("Tanaka")
print(f("how are you?")) # Hi Tanaka:how are you?

例2は add_msg 関数がクロージャで name 変数を記憶したものが func 関数から生成されています。

# 例2の続き
print(f.__code__.co_freevars[0], f.__closure__[0].cell_contents) # name Tanaka

関数 f に記憶されている変数名とその値を確認できます。
尚、クロージャには当然2個以上の変数も記憶できるので、エンクロージャの環境を記憶すると説明されることがあります。

自由変数 と 束縛(バインド)

Pythonの自由変数とはある関数内でローカル変数と自身の引数以外で使われている変数です。
例1で x は add3 の中において 自由変数 です。
同様に例2で name は add_msg にとって 自由変数です。

束縛とは名前を定義することです。
n = "Tanaka" とすれば この代入対象の識別子 n は 束縛されます。
関数を定義すれば関数名が束縛されますし、クラスを定義すればクラス名が束縛されます。
import文ではモジュール名が束縛されます。
つまり名前が何らかのオブジェクトに紐付けされることです。
また自由変数がクロージャ内に記憶されることをクロージャに束縛されると言います。

束縛された変数の更新

# 例3
def func():
    x = 3
    def value(v):
        nonlocal x
        x = v
    def add(y):
        return y+x
    x = 5
    return value, add

v, f = func()
print(f(4)) # 9 = 5 + 4 
v(10) # x の値が 5 → 10 に更新される
print(f(4)) # 14 = 4 + 10

束縛された変数の値を後から更新する必要がある場合は Python 3 で導入された nonlocal で宣言します。
例3では自由変数 x を nonlocal で宣言しておくことで後から value 関数を経由して add 関数内でも束縛されている x を更新しています。

# 例3の続き
print(v.__closure__[0].cell_contents is f.__closure__[0].cell_contents) # True

クロージャvで束縛されている変数 x とfで束縛される変数 x は同一オブジュエクトであることが判ります。

静的(レキシカル)スコープ

クロージャは静的スコープの言語で成り立ちます。
静的スコープとは変数の名前解決がソースコード上で成立するものいいます。
C, Python, Javascriptなどは静的スコープの言語です。
逆に動的スコープとは変数の名前解決が実行時のコースタックにて行われます。
Emacs Lisp, bash シェルスクリプト, powershell などは動的スコープです。
PerlやLispでは定義時に静的スコープか動的スコープかを選択できます。

66
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
66
Help us understand the problem. What is going on with this article?