Python のクロージャーについて話す - Closure

高洛峰
リリース: 2016-11-01 11:27:09
オリジナル
951 人が閲覧しました

Python のクロージャはすぐに理解できる概念ではありませんが、学習を深めていくと、どうしてもそのようなことを理解する必要があります。

クロージャの概念

クロージャを概念的に理解してみましょう。

一部の言語では、関数内に別の関数を (ネストして) 定義できる場合、内部関数が外部関数の変数を参照すると、クロージャが発生することがあります。クロージャを使用すると、関数と一連の「プライベート」変数の間の関連付けを作成できます。これらのプライベート変数は、特定の関数への複数の呼び出しにわたって永続性を維持します。

——Wikipedia)

わかりやすく言うと、関数がオブジェクトとして返されると、外部変数が取り込まれてクロージャーを形成します。例を参照してください。

def make_printer(msg): 
    def printer(): 
        print msg  # 夹带私货(外部变量) 
    return printer  # 返回的是函数,带私货的函数 
 
printer = make_printer('Foo!') 
printer()
ログイン後にコピー

関数をオブジェクトとして使用することをサポートするプログラミング言語は、通常、クロージャをサポートします。 Python、JavaScriptなど。

クロージャを理解する方法

クロージャの意味は何ですか?なぜクロージャが必要ですか?

クロージャの意味は、外部変数(プライベートグッズ)を運ぶことだと個人的には考えています。通常の関数と何ら変わりません。同じ機能が、異なる機能を実現するために異なる私有物を運ぶ。実際、クロージャの概念は、インターフェイス指向プログラミングの概念と非常に似ており、軽量のインターフェイスのカプセル化として理解することもできます。

インターフェイスは、メソッド シグネチャの一連の制約ルールを定義します。

def tag(tag_name): 
    def add_tag(content): 
        return "<{0}>{1}</{0}>".format(tag_name, content) 
    return add_tag 
 
content = &#39;Hello&#39; 
 
add_tag = tag(&#39;a&#39;) 
print add_tag(content) 
# <a>Hello</a> 
 
add_tag = tag(&#39;b&#39;) 
print add_tag(content) 
# <b>Hello</b>
ログイン後にコピー

この例では、コンテンツにタグを追加する関数が必要ですが、具体的な tag_name は実際のニーズに基づいて決定されます。外部呼び出しのインターフェイスは add_tag(content) です。インターフェース指向で実装する場合は、まずインターフェースとして add_tag を記述し、そのパラメーターと戻り値の型を指定してから、a と b の add_tag をそれぞれ実装します。

しかし、クロージャの概念では、add_tag は 2 つのパラメータ、tag_name と content を必要とする関数ですが、パラメータ tag_name はパックされています。そこで、最初に梱包して持ち帰る方法を教えてください。

上記の例はあまり鮮明ではありませんが、実際、クロージャの概念は私たちの生活や仕事の中でも非常に一般的です。たとえば、携帯電話でダイヤルするときは、通話の宛先だけを気にし、携帯電話の各ブランドがどのように実装しているか、どのモジュールが使用されているかについては気にしません。別の例としては、レストランに食事をするときに、料金を支払う必要があるのはサービスを楽しむためだけであり、その食事にどれだけのガター油が使用されたかわかりません。これらは、いくつかの機能またはサービス (電話をかける、食事など) を返すクロージャとみなすことができますが、これらの関数は外部変数 (アンテナ、オイルの排出など) を使用します。

このクラスを構築するときは、さまざまなパラメーターを使用します。これらのパラメーターは、このクラスによって提供される外部メソッドです。ただし、クラスはクロージャよりもはるかに大きくなります。クロージャは実行できる単なる関数ですが、クラス インスタンスは多くのメソッドを提供する可能性があるためです。

クロージャを使用する場合

実際、クロージャは Python では非常に一般的ですが、これがクロージャであるという事実に特別な注意を払っていなかっただけです。たとえば、Python のデコレータでは、パラメータを使用してデコレータを記述する必要がある場合、通常はクロージャが生成されます。

なぜでしょうか? Python のデコレータは固定関数インターフェイス形式だからです。デコレータ関数 (またはデコレータ クラス) が関数を受け入れて関数を返す必要があります:

# how to define 
def wrapper(func1):  # 接受一个callable对象 
    return func2  # 返回一个对象,一般为函数 
     
# how to use 
def target_func(args): # 目标函数 
    pass 
 
# 调用方式一,直接包裹 
result = wrapper(target_func)(args) 
 
# 调用方式二,使用@语法,等同于方式一 
@wrapper 
def target_func(args): 
    pass 
 
result = target_func()
ログイン後にコピー

それでは、デコレータがパラメータを受け取る場合はどうなるでしょうか? 次に、これらのパラメータを受け取るために使用される、元のデコレータ上に別のレイヤーをラップする必要があります。これらのパラメータ (プライベート グッズ) が内部デコレータに渡された後、クロージャが形成されます。したがって、デコレータがカスタム パラメータを必要とする場合、通常はクロージャが形成されます。 (クラス デコレータは例外)

def html_tags(tag_name): 
    def wrapper_(func): 
        def wrapper(*args, **kwargs): 
            content = func(*args, **kwargs) 
            return "<{tag}>{content}</{tag}>".format(tag=tag_name, content=content) 
        return wrapper 
    return wrapper_ 
 
@html_tags(&#39;b&#39;) 
def hello(name=&#39;Toby&#39;): 
    return &#39;Hello {}!&#39;.format(name) 
 
# 不用@的写法如下 
# hello = html_tag(&#39;b&#39;)(hello) 
# html_tag(&#39;b&#39;) 是一个闭包,它接受一个函数,并返回一个函数 
 
print hello()  # <b>Hello Toby!</b> 
print hello(&#39;world&#39;)  # <b>Hello world!</b>
ログイン後にコピー

デコレータのより詳細な分析については、私が書いた別のブログを読むことができます。

もう少し詳しく見てみましょう

実際、上記の概念を理解していれば、頭痛の種と思われる多くのコードはそれだけで済みます。

クロージャ パッケージがどのようなものかを見てみましょう。実際、クロージャ関数には通常の関数と比較して追加の __closure__ 属性があり、すべてのセル オブジェクトを格納するタプルを定義し、各セル オブジェクトはすべての外部変数を 1 つずつクロージャに格納します。

>>> def make_printer(msg1, msg2): 
    def printer(): 
        print msg1, msg2 
    return printer 
>>> printer = make_printer(&#39;Foo&#39;, &#39;Bar&#39;)  # 形成闭包 
 
>>> printer.__closure__   # 返回cell元组 
(<cell at 0x03A10930: str object at 0x039DA218>, <cell at 0x03A10910: str object at 0x039DA488>) 
 
>>> printer.__closure__[0].cell_contents  # 第一个外部变量 
&#39;Foo&#39; 
>>> printer.__closure__[1].cell_contents  # 第二个外部变量 
&#39;Bar&#39;
ログイン後にコピー

原理はとてもシンプルです。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート