2

好奇心から、関数を他の関数に明示的に渡すか、関数に内部から関数を呼び出させることがより望ましいです。これは、明示的な場合の方が暗黙的な場合よりも優れていますか?

たとえば(以下は私が何を意味するかを説明するためだけのものです)

def foo(x,y):
    return 1 if x > y else 0

partialfun = functools.partial(foo, 1)

def bar(xs,ys):
    return partialfun(sum(map(operator.mul,xs,ys)))

>>> bar([1,2,3], [4,5,6])

- また -

def foo(x,y):
    return 1 if x > y else 0

partialfun = functools.partial(foo, 1)

def bar(fn,xs,ys):
    return fn(sum(map(operator.mul,xs,ys)))

>>> bar(partialfun, [1,2,3], [4,5,6])
4

3 に答える 3

2

この状況では、関数と他のものとの間に実際には何の違いもありません。関数の呼び出しごとに異なる可能性のあるパラメーターである場合は、引数として何かを渡します。呼び出している関数(このbar例では)が常に同じ他の関数を呼び出している場合、それを引数として渡す理由はありません。多くの異なる関数を使用できるようにパラメーター化する必要がある場合(つまり、bar以外にも多くの関数を呼び出す必要があり、どの関数を呼び出すpartialfunかを知る必要がある場合)、引数として渡す必要があります。

于 2012-08-22T22:52:53.197 に答える
2

一般的にはそうですが、いつものように、それは異なります。ここで説明しているのは、依存性注入として知られています。一般に、特定の関数のロジックから変動性を分離できるため、これは良い考えです。これは、たとえば、そのようなコードをテストするのが非常に簡単になることを意味します。

# To test the process performed in bar(), we can "inject" a function
# which simply returns its argument
def dummy(x):
    return x

def bar(fn,xs,ys):
    return fn(sum(map(operator.mul,xs,ys)))

>>> assert bar(dummy, [1,2,3], [4,5,6]) == 32
于 2012-08-22T22:53:02.467 に答える
2

それは文脈に大きく依存します。

基本的に、関数がの引数である場合、barその関数を実装する方法を知るのは呼び出し元の責任です。bar気にする必要はありません。しかし、その結果、barのドキュメントには、必要な機能の種類を説明する必要があります。

多くの場合、これは非常に適切です。明らかな例はmap組み込み関数です。mapリスト内の各アイテムに関数を適用し、結果のリストを返すロジックを実装します。mapそれ自体は、アイテムが何であるか、または関数がそれらに対して何をしているのかを知りませんし、気にしません。mapのドキュメントには、1つの引数の関数が必要であると記載されている必要があり、の各呼び出し元はmap、適切な関数を実装または検索する方法を知っている必要があります。しかし、この配置は素晴らしいです。これにより、カスタムオブジェクトのリストと、それらのオブジェクトを具体的に操作する関数を渡すことができ、map離れてその一般的なことを実行できます。

しかし、多くの場合、この配置は不適切です。関数は、高レベルの操作に名前を付け、内部実装の詳細を非表示にするため、操作を1つの単位と考えることができます。その操作の一部を関数パラメーターとして外部から渡すことを許可すると、その操作がその関数のインターフェースを使用する方法で機能することが明らかになります。

より具体的な(多少工夫されていますが)例が役立つ場合があります。Personとを表すデータ型を実装し、誰かのフルネームと役職を文字列にフォーマットしたり、クライアントコードを電子メールの署名やレターヘッドなどに挿入したりするためJobの関数を作成しているとします。name_and_title明らかにとが必要にPersonなりJobます。呼び出し元が人の名前をフォーマットする方法を決定できるようにするために、関数パラメーターをとる可能性がありますlambda firstname, lastname: lastname + ', ' + firstname。しかし、これを行うには、私が人々の名前を別々の名と姓で表していることを明らかにすることです。ミドルネームのサポートに変更したい場合は、name_and_titleミドルネームを含めることができないか、受け入れる関数のタイプを変更する必要があります。名前が4つ以上あることに気づき、名前のリストを格納するように変更することにした場合は、name_and_title受け入れる関数のタイプを必ず変更する必要があります。

したがって、あなたのbar例では、どちらが良いかはわかりません。これは、意味のない抽象的な例だからです。それは、toの呼び出しが、実行することになっていることpartialfunの実装の詳細であるbarかどうか、またはpartialfun呼び出しが呼び出し元が知っている(そして何か他のことをしたいと思うかもしれない)ものであるかどうかによって異なります。それが「の一部」である場合bar、それはパラメータであってはなりません。呼び出し元の「一部」である場合は、パラメーターである必要があります。

膨大な数の関数パラメーターをbar持つ可能性があることは注目に値します。、、、およびを呼び出します。これらはすべてパラメータ化して、より柔軟にすることができます。summapoperator.mulbar

def bar(fn, xs,ys, g, h, i):
    return fn(g(h(i,xs,ys))

そして、gの出力で呼び出される方法hも抽象化できます。

def bar(fn, xs, ys, g, h, i, j):
    return fn(j(g, h(i, xs, ys)))

そして、bar何もしなくなるまで続けていくことができます。すべては渡された関数によって制御されます。呼び出し元は、100個の関数を記述して実行するのではなく、実行したいことを直接実行したほうがよいでしょう。そしてそれらをに渡しbarて関数を実行します。

ですから、常に当てはまる明確な答えはありません。それはあなたが書いている特定のコードに依存します。

于 2012-08-23T02:48:49.890 に答える