Python学習ノートの関数を戻り値、閉パッケージとして解釈する


関数を戻り値として使用
高次関数はパラメータとして関数を受け入れるほか,結果値として関数を返すこともできる.
可変パラメータの和を実現する.通常、和を求める関数は次のように定義されます.
def calc_sum(*args):
    ax = 0
    for n in args:
        ax = ax + n
    return ax

しかし、すぐに和を求める必要がなく、後のコードで必要に応じて再計算したらどうなりますか?和を求める結果ではなく、和を求める関数を返すことができます!
def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

lazyを呼び出すとsum()の場合、返されるのは和を求める結果ではなく、和を求める関数です.
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function sum at 0x10452f668>

関数fを呼び出すと、加算の結果が本当に計算されます.
>>> f()
25

この例では関数lazy_sumには関数sumも定義されており、内部関数sumはlazy_sumが関数sumを返すと,関連パラメータと変数が返される関数に保存され,この「クローズドパッケージ」と呼ばれるプログラム構造は大きな威力を持つ.
lazyを呼び出すとsum()の場合、同じパラメータが入力されても、呼び出されるたびに新しい関数が返されます.
>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False

f 1()とf 2()の呼び出し結果は互いに影響しない.
クローズドパッケージ
返される関数は、定義された内部でローカル変数argsを参照していることに注意してください.したがって、1つの関数が1つの関数を返すと、その内部のローカル変数は新しい関数にも参照されるので、閉パッケージは簡単で、実現するのは容易ではありません.
もう1つの注意すべき問題は、返される関数がすぐに実行されるのではなく、f()が呼び出されるまで実行されないことである.例を見てみましょう
def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()

上記の例では、ループごとに新しい関数が作成され、作成した3つの関数が返されます.
f 1()、f 2()、f 3()を呼び出すと、結果は1、4、9になると思いますが、実際の結果は:
>>> f1()
9
>>> f2()
9
>>> f3()
9

全部9!なぜなら、返される関数は変数iを参照しているが、すぐに実行されるわけではないからである.3つの関数が返されると、参照される変数iは3になり、最終結果は9になります.
閉パッケージを返すときに覚えておくのは、戻り関数がループ変数を参照しないか、その後に変化する変数を参照しないことです.
ループ変数を参照する必要がある場合はどうしますか?方法は、ループ変数の現在の値をバインドする関数をもう1つ作成します.ループ変数がその後どのように変更されても、関数パラメータにバインドされた値は変わりません.
>>> def count():
...     fs = []
...     for i in range(1, 4):
...         def f(j):
...             def g():
...                 return j*j
...             return g
...         fs.append(f(i))
...     return fs
... 
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9

欠点はコードが長く,lambda関数を用いてコードを短縮できることである.