【Python】状態を持った関数、クロージャ―の作り方、使い方【入門第52回】

168, 2019-09-12

目次

クロージャ―とは?

こんにちは、narupoです。

今回はPythonでクロージャ―を使う方法を解説していきたいと思います。
クロージャ―とは一体何なのか?
クロージャ―は、状態を持った関数と言えます。
好きな時に好きなところに手軽に書けるクラスに似てるやつです。

この記事では具体的には↓を見ていきます。

  • クロージャ―の書き方

  • クロージャ―に状態を持たせる

  • クロージャ―の状態を外部から初期化する

  • クロージャ―に引数を渡す

クロージャ―の書き方

クロージャ―は↓のように書きます。

def outer():
    def inner():
        print('ハロー!')

    return inner

closure = outer()
closure()

1つずつ見ていきましょう。
まずouterという関数を書きます。

def outer():
    pass

そして、outerの中にinnerという関数を書きます。

def outer():
    def inner():
        print('ハロー!')

そしてouterの最後でinnerreturnします。

def outer():
    def inner():
        print('ハロー!')

    return inner

これでクロージャ―の完成です。
これを使うには↓のようにしてouterを呼び出してクロージャ―を作成します。
あとはクロージャ―を呼び出すだけです。

closure = outer()
closure()

↑のコードの実行結果は↓のようになります。

ハロー!

なんだこれは

ふつうの関数と何がちがうの

クロージャ―に状態を持たせる

先ほどのクロージャ―は実は未完成です。
クロージャ―は状態を持っていないといけません。
先ほどのクロージャ―に状態を持たせてみましょう。

↓のようにしてouter内に変数_xを書きます。

def outer():
    _x = 0
    def inner():
        print('ハロー!')

    return inner

あとはこの変数_xを、inner関数で参照するようにします。
inner内ではこの変数_xをインクリメントするようにしましょう。

def outer():
    _x = 0
    def inner():
        nonlocal _x
        _x += 1
        return _x

    return inner

nonlocalというのはinnerからouterの変数_xを参照するのに必要な宣言です。
あとはこれを↓のようにして呼び出してみましょう。

closure = outer()
print(closure())
print(closure())
print(closure())
print(closure())

↑のコードの実行結果は↓のようになります。

1
2
3
4

注目してほしいのは、_xの値です。closure()を実行するごとに、つまりinnerを呼び出すごとに_xの値が1ずつ増えていっています。
これはつまり、変数_xがクロージャ―の中で生き続けていることを意味します
これを、クロージャ―が変数_xという状態を持っていると言います。

このように、クロージャ―は関数なのに状態を持つことができます。
クラスなどは属性に状態を持たせることは出来ましたが、それと似ていますね。

クロージャ―の状態を外部から初期化する

先ほどのクロージャ―をさらに改造したいと思います。
カウントを始める値は開発者側で指定できた方が便利です。
そこで↓のようにして関数の引数で設定するようにします。

def create_counter(x):
    _x = x
    def counter():
        nonlocal _x
        _x += 1
        return _x

    return counter

counter_1 = create_counter(10)
counter_2 = create_counter(100)

print('counter_1', counter_1())
print('counter_1', counter_1())
print('counter_1', counter_1())

print('counter_2', counter_2())
print('counter_2', counter_2())
print('counter_2', counter_2())

↑のコードの実行結果は↓のようになります。

counter_1 11
counter_1 12
counter_1 13
counter_2 101
counter_2 102
counter_2 103

このようにクロージャ―は複数作ることができます。
そしてそれぞれのクロージャ―は関数の引数で状態を初期化することが可能です。
ここまで来るとなかなか便利そうな感じがしますね。

クロージャ―に引数を渡す

もちろんクロージャ―はただの関数ですから、引数を渡すことができます。
さきほどのクロージャ―を↓のように改造してみます。

def create_counter(x):
    _x = x
    def counter(y=1):
        nonlocal _x
        _x += y
        return _x

    return counter

これは↓のようにして使うことができます。

counter = create_counter(0)
print(counter())
print(counter(2))
print(counter(4))
print(counter(8))

↑のコードの実行結果は↓のようになります。

1
3
7
15

おわりに

クロージャ―はなかなか便利ですが、クラスの万能さに隠れがちな子です。
みなさんも使ってあげてください。

以上、次回に続きます。

また見てね

関連動画

スポンサーリンク

スポンサーリンク

スポンサーリンク

スポンサーリンク