[python]Skill of code-インタフェースが単純な場合、クラスではなく関数を受信します.


インタフェースが簡単であれば、クラスではなく関数を受信します。


ほとんどのPython埋め込みAPIでは、この機能はアクションを関数の外にカスタマイズできます.APIは、このフックを使用して、ある人が作成したコードを実行するときに呼び出されます.
例えば、リストタイプのsortメソッドは、ソートに必要な各インデックスの値を決定するためにオプションのキーパラメータを受信する.次のコードはlambda式をkeyフックに渡し、名前リストを長さで並べ替えます.


names = ['Socrates', 'Archimedes', 'Plato', 'Aristotle']
names.sort(key=lambda x: len(x)) # 문자의 길이만큼

print(names)
['Plato', 'Socrates', 'Aristotle', 'Archimedes']
他の言語では、フックを抽象クラスとして定義することが予想されます.しかし、Pythonのフックでは、ほとんどがパラメータと戻り値を定義した単純な無状態関数である.関数はクラスよりも記述しやすく、定義も簡単で、フックに適しています.関数がフックを使用するのは、Pythonが1級関数を持っているためです.言い換えれば、他の値を伝達および参照するように、言語で関数およびメソッドを伝達することができるからである.
はい.defaultdictクラスのアクションをカスタマイズしてみましょう.見つからない鍵にアクセスするたびに、データ構造は呼び出された関数を受信します.defaultdictに渡される関数は、dickshernerで見つからないキーに対応するデフォルト値を返さなければなりません.次に、キーが見つからないたびにログを残し、デフォルトで0を返すフックを定義するコードを示します.


from collections import defaultdict # 외부 함수이기 때문에 import해야한다.

def log_missing():
    print('Key added')
    return 0
ログを出力するには、初期値を含むディックシリーズと必要なインクリメンタルリストを使用してlog missing関数(それぞれ「red」と「orange」)を2回実行します.

current = {'green': 12, 'blue': 3}
increments = [
    ('red', 5),
    ('blue', 17),
    ('orange', 9),
]
result = defaultdict(log_missing, current)
print('Before:', dict(result))
for key, amount in increments:
    result[key] += amount
print('After: ', dict(result))
Before: {'green': 12, 'blue': 3}
Key added
Key added
After:  {'green': 12, 'blue': 20, 'red': 5, 'orange': 9}
log missingのような関数をスキップすると,決定挙動と副作用が分離されるため,APIの構築とテストが容易になる.たとえば、デフォルトのフックをdefaultdictに渡して見つからない鍵の総数を計算するとします.これは、ステータス保持モジュールを使用し、次にステータス保持モジュールをデフォルトのフックのhelper関数として使用します.

def increment_with_report(current, increments):
    added_count = 0

    def missing():
        nonlocal added_count  # Stateful closure
        added_count += 1
        return 0

    result = defaultdict(missing, current)
    for key, amount in increments:
defaultdictはmissingフックがステータスを保持することを知らないが、increment with report関数を実行するとtupleが要素として予想される個数2が得られる.これは,インタフェースとして単純な関数を用いる場合に得られるもう一つの利点である.ステータスをエンクロージャに隠すと、後で機能を追加しやすくなります.
result, count = increment_with_report(current, increments)
assert count == 2
ステータス保持フックのモジュールを定義するときに発生する問題は、ステータス関数のない例よりも理解しにくい.もう1つの方法は、保存するステータスをカプセル化するために小さなクラスを定義することです.
class CountMissing(object):
    def __init__(self):
        self.added = 0

    def missing(self):
        self.added += 1
        return 0
他の言語では、CountMissingのインタフェースを受け入れるためにdefaultdictを変更する必要があると考えられます.ただし、Pythonでは、一級関数のため、カウントを対象としている.missingメソッドを直接参照し、defaultdictのデフォルトのフックに渡すことができます.メソッドが関数インタフェースを満たすことは言うまでもない.
current = {'green': 12, 'blue': 3}
increments = [
    ('red', 5),
    ('blue', 17),
    ('orange', 9),
]

counter = CountMissing()
result = defaultdict(counter.missing, current)  # Method reference
    result[key] += amount
assert counter.added == 2
print(result)
defaultdict(<bound method CountMissing.missing of <__main__.CountMissing object at 0x0000020A8A0F7CD0>>, {'green': 12, 'blue': 20, 'red': 5, 'orange': 9})
helperクラスを使用してステータス保持モジュールの動作を提供する方法は、前にincrement with report関数を使用した方法よりも明確です.しかし,CountMissingクラスだけではその用途は理解し難い.
CountMissingオブジェクトを作成するのは誰ですか?誰がmissingメソッドを呼び出しますか?
後で他の公開メソッドをクラスに追加する必要がありますか?このクラスはdefaultdictに関連付けられて使用される例を見る前に謎になります.
Pythonでは、クラスでmagicメソッド呼び出しを定義して、この状況を明確にすることができます.
callメソッドでは、関数のようにオブジェクトを呼び出すことができます.また、組み込み関数callableは、これらのインスタンスをTrueに戻します.
class BetterCountMissing(object):
    def __init__(self):
        self.added = 0

    def __call__(self):
        self.added += 1
        return 0

counter = BetterCountMissing()
counter()
次のコードは、DefaultDictのデフォルトのフックとしてBenterCountMissingインスタンスを使用して、DickShownerが不足しているために追加された新しい鍵の数を理解します.

counter = BetterCountMissing()
result = defaultdict(counter, current)  # Relies on __call__
for key, amount in increments:
    result[key] += amount
assert counter.added == 2
print(result)
defaultdict(<__main__.BetterCountMissing object at 0x0000020A8A06FA60>, {'green': 12, 'blue': 20, 'red': 5, 'orange': 9})
この例はCountMissingです.missingの例よりも明確です.callメソッド(APIフックなど)は、クラスのインスタンスが関数パラメータを適切な場所で使用できることを示します.このコードを初めて見た人をクラスの主要な動作を担当するエントリポイントに導くこともできます.強いプロンプトクラスの目的は、ステータス保持モジュールを使用することです.
最も重要なのは、callを使用してもdefaultdictが何が起こるか分からないことです.defaultdictに必要なのは、デフォルトのフックに使用される関数だけです.Pythonは、単純な関数インタフェースの要件を満たすための多くの方法を提供しています.

コアの整理

  • Pythonでは、コンポーネント間の単純なインタフェースのクラスを定義し、インスタンスを作成するには、通常、関数だけで十分です.
  • メソッドの参照は1級である.つまり、他のタイプのように式で使用することができます.
  • callという特殊な方法は、クラスのインスタンスを通常のPython関数のように呼び出すことができる.
  • ステータスの関数を保持する必要がある場合は、ステータス保持モジュールcallメソッドを定義するのではなく、クラスを定義することを考慮します.