Python でのクロージャの使用例

黄舟
黄舟オリジナル
2017-10-02 19:43:431050ブラウズ

この記事では主に Python 基本チュートリアルでのクロージャの使い方に関する情報を紹介しますので、困っている方は

Python 基本チュートリアルでのクロージャの使い方

を参考にしていただければ幸いです。前書き:

クロージャは関数型プログラミングにおける重要な文法構造です。関数型プログラミングはプログラミング パラダイムです (手続き型プログラミングとオブジェクト指向プログラミングもプログラミング パラダイムです)。プロセス指向プログラミングでは関数を見てきましたが、オブジェクト指向プログラミングではオブジェクトを見てきました。関数とオブジェクトの基本的な目的は、コードを特定の論理的な方法で編成し、コードの再利用性を向上させることです。クロージャはコードを整理するための構造でもあり、コードの再利用性も向上します。

異なる言語は異なる方法でクロージャを実装します。 Python は関数オブジェクトに基づいており、クロージャの構文構造のサポートを提供します (Python がオブジェクトを使用して、特殊なメソッドやマルチパラダイムで特殊な構文を実装していることを何度も見てきました)。 Python ではすべてがオブジェクトであり、関数の文法構造もオブジェクトです。関数オブジェクトでは、関数オブジェクトの名前を変更したり、関数オブジェクトをパラメータとして渡したりするなど、通常のオブジェクトと同じように関数オブジェクトを使用します。

関数オブジェクトのスコープ

他のオブジェクトと同様、関数オブジェクトにも独自の生存スコープがあり、それが関数オブジェクトのスコープです。関数オブジェクトは def ステートメントを使用して定義され、関数オブジェクトのスコープは def が配置されているレベルと同じです。たとえば、次のコードでは、line_conf 関数の所属範囲内で定義した関数行は、line_conf の所属範囲内でのみ呼び出すことができます。


def line_conf():
  def line(x):
    return 2*x+1
  print(line(5))  # within the scope


line_conf()
print(line(5))    # out of the scope

line 関数は直線 (y = 2x + 1) を定義します。ご覧のとおり、line 関数は line_conf() で呼び出すことができますが、スコープ外で line を呼び出すと、既にスコープ外にあることを示す


NameError: name 'line' is not defined

というエラーが発生します。

同様に、ラムダを使用して関数を定義する場合、関数オブジェクトのスコープはラムダが配置されているレベルと同じになります。

クロージャ

関数はオブジェクトなので、特定の関数の戻り結果として使用できます。


def line_conf():
  def line(x):
    return 2*x+1
  return line    # return a function object

my_line = line_conf()
print(my_line(5))

上記のコードは正常に実行できます。 line_confの戻り結果はlineオブジェクトに代入されます。上記のコードは 11 を出力します。

line() の定義で外部変数が参照された場合はどうなりますか?


def line_conf():
  b = 15
  def line(x):
    return 2*x+b
  return line    # return a function object

b = 5
my_line = line_conf()
print(my_line(5))

lineで定義された下位プログラムブロック内で上位変数bが参照されていることがわかりますが、bの情報はline定義の外に存在します(bの定義はlineの下位プログラムブロック内にありません)ライン)。 b を line の環境変数と呼びます。実際、line が line_conf の戻り値として使用される場合、line にはすでに b の値が含まれています (ただし、b は line に関連付けられていません)。

上記のコードは 25 を出力します。つまり、line で参照される b 値は、使用時の b 値ではなく、関数オブジェクトの定義時に参照できる b 値

です。 関数とその環境変数は一緒になって

クロージャを形成します。

Python では、いわゆるクロージャは、環境変数の値を含む関数オブジェクトです。環境変数の値は、関数オブジェクトの __closure__ 属性に格納されます。たとえば、次のコード:

def line_conf():
  b = 15
  def line(x):
    return 2*x+b
  return line    # return a function object

b = 5
my_line = line_conf()
print(my_line.__closure__)
print(my_line.__closure__[0].cell_contents)

__closure__ にはタプルが含まれています。このタプルの各要素はセル型のオブジェクトです。最初のセルには整数 15 が含まれていることがわかります。これは、クロージャを作成したときの環境変数 b の値です。

クロージャの実際的な例を見てみましょう:

def line_conf(a, b):
  def line(x):
    return ax + b
  return line

line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5), line2(5))

この例では、関数行と環境変数 a と b がクロージャを形成します。クロージャを作成するとき、line_conf のパラメータ a と b を通じてこれら 2 つの環境変数の値を指定しました。このようにして、関数の最終形式 (y = x + 1 および y = 4x + 5) を決定しました。 。パラメータ a と b を変換するだけで、異なる直線式関数が得られます。このことから、クロージャにはコードの再利用性を向上させる効果もあることがわかります。

クロージャがない場合、直線関数を作成するたびに a、b、x を指定する必要があります。この方法では、より多くのパラメーターを渡す必要があり、コードの移植性が低下します。クロージャを使用して、実際に関数を作成します。線関数は広義の関数を定義します。この関数の一部の側面はすでに決定されています (直線である必要があります) が、他の側面 (a および b パラメーターなど) は未決定です。続いて、line_conf によって渡されたパラメータに基づいて、クロージャの形式で最終関数を決定します。

クロージャと並列操作


クロージャは、関数に定義する必要があるパラメータの数を効果的に減らします。これは並列操作に重要な意味を持ちます。並列コンピューティング環境では、各コンピューターに機能を担当させ、あるコンピューターの出力を次のコンピューターの入力と連結できます。最終的には、一連のコンピューター クラスターの一方の端からデータを入力し、もう一方の端からデータを出力する、組み立てラインのように作業します。この状況は、パラメーター入力が 1 つだけの関数に最適です。クロージャはこの目的を達成できます。

並列コンピューティングがホットスポットになりつつあります。これは、関数型プログラミングが再び人気を集めている重要な理由でもあります。関数型プログラミングは 1950 年代には存在していましたが、あまり広く使用されていませんでした。ただし、上で説明したパイプライン化された作業並列クラスタリング プロセスは、関数型プログラミングに完全に適しています。関数型プログラミングの自然な利点により、ますます多くの言語が関数型プログラミング パラダイムをサポートし始めています。

以上がPython でのクロージャの使用例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。