原文:『実用的なPythonプログラミング』07_03_Returning_functions

5228 ワード

目次|前節(7.2匿名関数)|[次節(7.4デコレーション)]()
7.3戻り関数
このセクションでは、関数を使用して他の関数を作成する考え方について説明します.
概要
次の関数を考慮します.
def add(x, y):
    def do_add():
        print('Adding', x, y)
        return x + y
    return do_add

これは他の関数を返す関数です.
>>> a = add(3,4)
>>> a

>>> a()
Adding 3 4
7

ローカル変数
内部関数が外部関数で定義された変数をどのように参照するかを観察してください.
def add(x, y):
    def do_add():
        # `x` and `y` are defined above `add(x, y)`
        print('Adding', x, y)
        return x + y
    return do_add

さらに観察すると、add()関数が終了した後も、これらの変数は生存していることが分かった.
>>> a = add(3,4)
>>> a

>>> a()
Adding 3 4      # Where are these values coming from?
7

クローズドパッケージ
内部関数が結果として返される場合、内部関数は閉パケット(closure)と呼ばれます.
def add(x, y):
    # `do_add` is a closure
    def do_add():
        print('Adding', x, y)
        return x + y
    return do_add

≪基本プロパティ|Basic Properties|emdw≫:閉包は、関数が正常に動作した後に必要なすべての変数の値を保持します.閉パッケージは、依存する変数の値を保存するための追加の環境を持つ関数と見なすことができます.
クローズドパッケージの使用
閉パッケージはPythonの基本的な特性ですが、それらの使い方は通常微妙です.一般的なアプリケーション:
  • はコールバック関数で使用されます.
  • 遅延計算.
  • アクセラレータ関数(後述).

  • ちえんけいさん
    次のような関数を考慮します.
    def after(seconds, func):
        import time
        time.sleep(seconds)
        func()

    使用例:
    def greeting():
        print('Hello Guido')
    
    after(30, greeting)
    after(30秒遅延後)所定の関数を実行する......
    閉パッケージには他の情報が付属しています.
    def add(x, y):
        def do_add():
            print(f'Adding {x} + {y} -> {x+y}')
        return do_add
    
    def after(seconds, func):
        import time
        time.sleep(seconds)
        func()
    
    after(30, add(2, 3))
    # `do_add` has the references x -> 2 and y -> 3

    コード繰返し
    閉パケットは、コードの大量の重複を回避する技術としても使用することができる.
    練習する
    練習7.7:閉パッケージを使用して重複を避ける
    閉パケットのより強力な特性は、重複するコードを生成することである.タイプチェック付きのプロパティを定義した練習5.7コードをレビューします.
    class Stock:
        def __init__(self, name, shares, price):
            self.name = name
            self.shares = shares
            self.price = price
        ...
        @property
        def shares(self):
            return self._shares
    
        @shares.setter
        def shares(self, value):
            if not isinstance(value, int):
                raise TypeError('Expected int')
            self._shares = value
        ...

    コードを何度も入力するよりも、閉パッケージを使用して自動的にコードを作成します.typedproperty.pyファイルを作成し、次のコードをファイルに入れてください.
    # typedproperty.py
    
    def typedproperty(name, expected_type):
        private_name = '_' + name
        @property
        def prop(self):
            return getattr(self, private_name)
    
        @prop.setter
        def prop(self, value):
            if not isinstance(value, expected_type):
                raise TypeError(f'Expected {expected_type}')
            setattr(self, private_name, value)
    
        return prop

    次に、次のようなクラスを定義してみましょう.
    from typedproperty import typedproperty
    
    class Stock:
        name = typedproperty('name', str)
        shares = typedproperty('shares', int)
        price = typedproperty('price', float)
    
        def __init__(self, name, shares, price):
            self.name = name
            self.shares = shares
            self.price = price

    インスタンスを作成し、タイプチェックが有効かどうかを確認してください.
    >>> s = Stock('IBM', 50, 91.1)
    >>> s.name
    'IBM'
    >>> s.shares = '100'
    ... should get a TypeError ...
    >>>

    練習7.8:関数呼び出しの簡略化
    上記の例では、ユーザは、typedproperty('shares', int)のような呼び出しの方法が少し冗長であることを発見することができる(特に、複数回繰り返し呼び出した場合).次の定義をtypedproperty.pyファイルに追加してください.
    String = lambda name: typedproperty(name, str)
    Integer = lambda name: typedproperty(name, int)
    Float = lambda name: typedproperty(name, float)

    次に、Stockクラスを再記述して、次の関数を使用します.
    class Stock:
        name = String('name')
        shares = Integer('shares')
        price = Float('price')
    
        def __init__(self, name, shares, price):
            self.name = name
            self.shares = shares
            self.price = price

    あ、少しよくなりました.ここでのポイントは、閉パッケージおよびlambdaがコードを簡略化し、嫌なコードの重複を解消するためによく使用されることです.これはいつもいいです.
    練習7.9:実践に移すstock.pyファイルのStockクラスを再作成して、上記のタイプ化プロパティ(typed properties)を使用してください.
    目次|前節(7.2匿名関数)|[次節(7.4デコレーション)]()
    注:完全な翻訳はhttps://github.com/codists/practical-python-zhを参照