60
def maker(n):
    def action(x):
        return x ** n
    return action

f = maker(2)
print(f)
print(f(3))
print(f(4))

g = maker(3)
print(g(3))

print(f(3)) # still remembers 2

呼び出されるまでに戻って終了した2にもかかわらず、ネストされた関数が最初の値を記憶するのはなぜですか?maker()action()

4

9 に答える 9

40

基本的にクロージャーを作成しています。

コンピュータサイエンスでは、クロージャは、字句環境でバインドされた自由変数を持つ第一級関数です。このような関数は、その自由変数を「閉じた」と言われます。

関連資料:クロージャ:なぜそんなに便利なのですか?

クロージャは、関数にローカル状態へのアクセスを与えるためのより便利な方法です。

http://docs.python.org/reference/compound_stmts.htmlから:

プログラマーのメモ:関数はファーストクラスのオブジェクトです。関数定義内で実行される「def」フォームは、返されるか、または渡されることができるローカル関数を定義します。ネストされた関数で使用される自由変数は、defを含む関数のローカル変数にアクセスできます。詳細については、「命名とバインディング」のセクションを参照してください。

于 2010-01-05T12:37:17.340 に答える
33

親関数で発生したすべての変数が、子関数内の実際の値に置き換えられていることがわかります。このように、子関数を正しく実行するために親関数のスコープを追跡する必要はありません。

「動的に関数を作成する」と見なしてください。

def maker(n):
  def action(x):
    return x ** n
  return action

f = maker(2)
--> def action(x):
-->   return x ** 2

これはPythonの基本的な動作であり、複数の割り当てでも同じように動作します。

a = 1
b = 2
a, b = b, a

Pythonはこれを次のように読み取ります

a, b = 2, 1

基本的に、値を使用する前に値を挿入します。

于 2010-01-05T12:41:12.617 に答える
15

2つの関数を定義しています。電話するとき

f = maker(2)

2倍の数を返す関数を定義しているので、

f(2) --> 4
f(3) --> 6

次に、別の異なる関数を定義します

g = maker(3)

数の3倍を返す

g(3) ---> 9

しかし、それらは2つの異なる関数であり、参照される同じ関数ではなく、それぞれが独立した関数です。関数内のスコープでも「maker」は同じと呼ばれ、同じ関数ではありません。呼び出すたびにmaker()、異なる関数を定義します。これはローカル変数のようなもので、関数を呼び出すたびに同じ名前が付けられますが、異なる値を含めることができます。この場合、変数'action'には関数が含まれています(異なる場合があります)

于 2010-01-05T12:37:54.363 に答える
9

それが「クロージャー」と呼ばれるものです。簡単に言えば、関数をファーストクラスのオブジェクトとして扱うすべてではないにしてもほとんどのプログラミング言語では、関数がまだ生きている限り、関数オブジェクト内で使用される変数はすべて囲まれます(つまり記憶されます)。それを利用する方法を知っているなら、それは強力な概念です。

あなたの例では、ネストされたaction関数は変数を使用するnため、その変数の周りにクロージャを形成し、後の関数呼び出しのためにそれを記憶します。

于 2010-01-05T12:39:41.733 に答える
6

内部関数を作成する3つの一般的な理由を見てみましょう。

1.閉鎖と工場機能

変数がスコープ外になったり、関数自体が現在の名前空間から削除されたりしても、囲んでいるスコープの値は記憶されます。

def print_msg(msg):
    """This is the outer enclosing function"""

    def printer():
        """This is the nested function"""
        print(msg)

    return printer  # this got changed

それでは、この関数を呼び出してみましょう。

>>> another = print_msg("Hello")
>>> another()
Hello

それは珍しいことです。print_msg()関数は文字列で呼び出され、返された"Hello"関数は名前にバインドされましたanother。を呼び出すと、関数another()の実行はすでに終了していますが、メッセージはまだ記憶されていprint_msg()ます。一部のデータ("Hello")をコードに添付するこの手法は、Pythonではクロージャと呼ばれます。

クロージャを使用するのはいつですか?

では、クロージャは何に適しているのでしょうか?クロージャは、グローバル値の使用を回避し、何らかの形式のデータ隠蔽を提供します。また、問題に対するオブジェクト指向のソリューションを提供することもできます。クラスに実装するメソッドが少ない場合(ほとんどの場合1つのメソッド)、クロージャーは代替のより洗練されたソリューションを提供できます。参照

2.カプセル化:

カプセル化の一般的な概念は、内側の世界を外側の世界から隠して保護することです。ここでは、内側の関数は外側の関数の内側でのみアクセスでき、関数の外側で発生することから保護されます。

3.それを乾かしてください

おそらく、同じコードのチャンクを多数の場所で実行する巨大な関数があります。たとえば、ファイルを処理する関数を作成し、開いているファイルオブジェクトまたはファイル名のいずれかを受け入れたいとします。

def process(file_name):
    def do_stuff(file_process):
        for line in file_process:
            print(line)
    if isinstance(file_name, str):
        with open(file_name, 'r') as f:
            do_stuff(f)
    else:
        do_stuff(file_name)

詳細については、このブログを参照してください。

于 2015-09-07T11:11:50.927 に答える
2

関数を作成したときnはだっ2たので、関数は次のようになります。

def action(x):
    return x ** 2

f(3)を呼び出すと、xはに設定される3ため、関数は次のようになります。3 ** 2

于 2010-01-05T12:42:20.930 に答える
1

人々は閉鎖について正しく答えました。つまり、アクション内の「n」の有効な値は、「maker」が呼び出されたときの最後の値です。

これを克服する簡単な方法の1つは、freevar(n)を「action」関数内の変数にすることです。この関数は、実行された瞬間に「n」のコピーを受け取ります。

これを行う最も簡単な方法は、作成時にデフォルト値が「n」であるパラメータとして「n」を設定することです。関数のデフォルトパラメータは関数自体の属性であるタプル(この場合はaction.func_defaults)に格納されるため、この「n」の値は固定されたままです。

def maker(n):
    def action(x, k=n):
        return x ** k
    return action

使用法:

f = maker(2) # f is action(x, k=2)
f(3)   # returns 3^2 = 9
f(3,3) # returns 3^3 = 27
于 2010-01-05T13:13:08.960 に答える
1

1つの使用法は、パラメーターを維持する関数を返すことです。

def outer_closure(a):
    #  parm = a               <- saving a here isn't needed
    def inner_closure():
        #return parm
        return a              # <- a is remembered 
    return inner_closure

# set parm to 5 and return address of inner_closure function
x5 = outer_closure(5)
x5()
>5

x6 = outer_closure(6)
x6()
>6

# x5 inner closure function instance of parm persists 
x5()
>5
于 2017-02-12T21:28:59.847 に答える
0

defキーワードを使用して関数を作成すると、まさにそれが実行されます。つまり、新しい関数オブジェクトを作成し、それを変数に割り当てます。あなたが与えたコードでは、その新しい関数オブジェクトをactionと呼ばれるローカル変数に割り当てています。

2回目に呼び出すと、2番目の関数オブジェクトが作成されます。したがって、fは最初の関数オブジェクト(square-the-value)を指し、gは2番目の関数オブジェクト(cube-the-value)を指します。Pythonが「f(3)」を検出すると、「変数fを指す関数オブジェクトを実行し、値3を渡す」ことを意味します。fとg、および異なる関数オブジェクトであるため、異なる値を返します。

于 2010-01-05T16:04:01.857 に答える