2

Python で既にコンパイルされた関数の戻り値をインターセプト (および変更) する方法はありますか?

基本的な考え方は次のとおりです。関数があります

def a (n, acc):
    if n == 0: return acc
    return a (n - 1, acc + n)

次のように動作するように、パッチを適用したいと思います。

def a (n, acc):
    if n == 0: return acc
    return lambda: a (n - 1, acc + n)

2 番目のコード スニペットのように関数fを生成するなど、関数を記述することは可能ですか?f (a)

Python がそのソースを見つけて、新しくコンパイルされたパッチが適用された関数を返すことができるかどうかを介して関数inspectにパッチを適用できますが、これはあまり役に立ちません。

4

3 に答える 3

2

あなたが何を望んでいるかを私が正しく理解しているなら、それは理論的に不可能です。あなたが説明しているように見える変換は、コンパイルされた形式で保持されない可能性が高いソースコードの表面的な詳細に応じて、同等の機能に異なる影響を与えるものです。たとえば、特定の関数の次の 2 つのバージョンについて考えてみます。

def a (n, acc):
    print('called a(%d,%d)' % (n, acc))
    if n == 0: return acc
    return a (n - 1, acc + n)

def a (n, acc):
    print('called a(%d,%d)' % (n, acc))
    if n == 0: return acc
    ret = a (n - 1, acc + n)
    return ret

明らかに、それらは機能的に同一です。ソース コードでの唯一の違いは、前者はreturn特定の式を直接使用するのに対し、後者はその式の結果をローカル変数に保存してから、returnその変数を使用することです。コンパイルされた形式では、違いはまったくありません。

ここで、「パッチを適用した」バージョンを検討してください。

def a (n, acc):
    print('called a(%d,%d)' % (n, acc))
    if n == 0: return acc
    return lambda: a (n - 1, acc + n)

def a (n, acc):
    print('called a(%d,%d)' % (n, acc))
    if n == 0: return acc
    ret = a (n - 1, acc + n)
    return lambda: ret

明らかにこれらは大きく異なります: たとえば、nis3accis 0 の場合、前者は を出力called a(3,0)して返す関数を出力called a(2,3)して返す関数を出力し、 を出力called a(1,5)して返す関数を出力called a(0,6)して返します6が、後者は and を出力called a(3,0)called a(2,3)called a(1,5)andcalled a(0,6)を返します。を返す関数を返す関数 を返す関数6

より広い違いは、最初の「パッチを適用した」関数は、新しい戻り値が呼び出されるたびに計算の 1 つのステップを実行するのに対し、2 番目の「パッチを適用した」バージョンは、最初の呼び出し中に計算のすべてのステップを実行し、単純に系列を配置することです。娯楽のためのその後の呼び出しの。この違いは、副作用 (メッセージを出力する、スタックをオーバーフローさせるほど深く再帰するなど) がある場合は常に重要になります。呼び出し元が副作用を導入するかどうかも問題になる可能性があります。これらの関数はa、コードの他のビットが再定義されるまでのみ再帰的であることに注意してください。aはすでにすべての呼び出しを完了しています。

2 つの「パッチが適用されていない」バージョンを区別できないため、変換が意味する個別の「パッチが適用された」バージョンを生成することは明らかにできません。

于 2013-02-03T07:35:23.013 に答える
1

ご意見ありがとうございます。私は明らかなことを見ていませんでした:

def inject (f):
    def result (*args, **kwargs):
        return lambda: f (*args, **kwargs)
    return result

彼が私を正しい方向に押しやったので、私はdavidchambersの答えを受け入れました。

于 2013-02-03T07:18:53.660 に答える
0

ルークの答えは非常に詳細ですが、これはまだ役立つかもしれません:

>>> def f(*args, **kwargs):
...     return lambda: a(*args, **kwargs)
...
>>> f(10, 0)()
55
于 2013-02-03T07:08:03.227 に答える