8

怠惰な関数呼び出しをカプセル化するPythonの方法はありますか?それにより、関数を最初に使用するf()と、以前にバインドされた関数がg(Z)呼び出され、後続の呼び出しf()でキャッシュされた値が返されますか?

メモ化は完全に適合しない場合があることに注意してください。

私は持っています:

f = g(Z)
if x:
     return 5
elif y:
     return f
elif z:
     return h(f)

g(Z)コードは機能しますが、値が使用されている場合にのみ呼び出されるように再構築したいと思います。の定義を変更したくないので、キャッシュするg(...)Zは少し大きいです。

編集:私はそれfが関数でなければならないと思いましたが、そうではないかもしれません。

4

8 に答える 8

7

あなたがキャッシングを求めているのか、それとも遅延評価を求めているのか、私は少し混乱しています。後者については、 Alberto Bertogli によるモジュール lazy.py をチェックしてください。

于 2011-03-14T10:07:53.910 に答える
3

このデコレータを使ってみてください:

class Memoize:
    def __init__ (self, f):
        self.f = f
        self.mem = {}
    def __call__ (self, *args, **kwargs):
        if (args, str(kwargs)) in self.mem:
            return self.mem[args, str(kwargs)]
        else:
            tmp = self.f(*args, **kwargs)
            self.mem[args, str(kwargs)] = tmp
            return tmp

(デッドリンクから抽出: http://snippets.dzone.com/posts/show/4840 / https://web.archive.org/web/20081026130601/http://snippets.dzone.com/posts/show/ 4840 ) (ここにあります:関数の戻り値を単純にキャッシュするデコレータはありますか? by Alex Martelli)

編集:これはプロパティの形式の別のものです(を使用__get__http://code.activestate.com/recipes/363602/

于 2011-03-14T05:23:50.223 に答える
1

メモ化のためのデコレータはかなりたくさんあります。

http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize http://code.activestate.com/recipes/498110-memoize-decorator-with-o1-length-limited-lru-cache/ http:// code .activestate.com / recipes / 496879-memoize-decorator-function-with-cache-size-limit /

完全に一般的な解決策を考え出すことは、あなたが思っているよりも難しいです。たとえば、ハッシュできない関数の引数に注意する必要があり、キャッシュが大きくなりすぎないようにする必要があります。

怠惰な関数呼び出し(値が必要な場合にのみ関数が実際に評価される関数)を本当に探している場合は、おそらくそのためにジェネレーターを使用できます。

編集:つまり、あなたが本当に望んでいるのは、結局のところ遅延評価だと思います。これがおそらくあなたが探しているものであるライブラリです:

http://pypi.python.org/pypi/lazypy/0.5

于 2011-03-14T05:31:23.707 に答える
1

キャッシュデコレータを使用できます。例を見てみましょう

from functools import wraps

class FuncCache(object):
    def __init__(self):
        self.cache = {}

    def __call__(self, func):
        @wraps(func)
        def callee(*args, **kwargs):
            key = (args, str(kwargs))
            # see is there already result in cache
            if key in self.cache:
                result = self.cache.get(key)
            else:
                result = func(*args, **kwargs)
                self.cache[key] = result
            return result
        return callee

キャッシュデコレータを使用すると、ここに次のように書くことができます

my_cache = FuncCache()

@my_cache
def foo(n):
    """Expensive calculation

    """
    sum = 0
    for i in xrange(n):
        sum += i
    print 'called foo with result', sum
    return sum

print foo(10000)
print foo(10000)
print foo(1234)

出力からわかるように

called foo with result 49995000
49995000
49995000

foo は一度だけ呼び出されます。関数 foo のどの行も変更する必要はありません。それがデコレータの力です。

于 2011-03-14T05:35:25.710 に答える
1

完全を期すために、ここに私の怠惰な評価者デコレータのレシピへのリンクがあります:

https://bitbucket.org/jsbueno/metapython/src/f48d6bd388fd/lazy_decorator.py

于 2012-04-02T14:24:13.860 に答える
1

これは非常に簡単な遅延デコレータですが、使用していません@functools.wraps(実際には のインスタンスとLazyその他の潜在的な落とし穴を返します)

class Lazy(object):
    def __init__(self, calculate_function):
        self._calculate = calculate_function

    def __get__(self, obj, _=None):
        if obj is None:
            return self
        value = self._calculate(obj)
        setattr(obj, self._calculate.func_name, value)
        return value


# Sample use:

class SomeClass(object):

    @Lazy
    def someprop(self):
        print 'Actually calculating value'
        return 13


o = SomeClass()
o.someprop
o.someprop
于 2014-08-18T09:49:39.597 に答える
0

あなたの編集と一連のdetlyのコメントの後でも、私はまだよく理解していません. 最初の文では、f() の最初の呼び出しは g() を呼び出すはずですが、その後キャッシュされた値を返すと言っています。しかし、あなたのコメントでは、「g()は何があっても呼び出されない」と言っています(私の強調)。何を否定しているのかわかりません: g() は決して呼び出されるべきではないと言っているのですか (あまり意味がありません; なぜ g() が存在するのですか?); または、g()が呼び出される可能性がありますが、呼び出されない可能性があります (これは、f() への最初の呼び出しで g() が呼び出されることにまだ矛盾しています)。次に、g() をまったく含まず、質問の最初の文にも、detly のコメント スレッドにも実際には関係のないスニペットを指定します。

もう一度編集する場合に備えて、私が返信しているスニペットは次のとおりです。

私は持っている:

a = f(Z)
if x:
     return 5
elif y:
     return a
elif z:
     return h(a)

コードは機能しますが、値が使用された場合にのみ f(Z) が呼び出されるように再構築したいと考えています。f(...) の定義を変更したくありません。また、Z はキャッシュするには少し大きいです。

それが本当にあなたの質問なら、答えは簡単です

if x:
    return 5
elif y:
    return f(Z)
elif z:
    return h(f(Z))

これが、「値が使用されている場合にのみ f(Z) が呼び出される」ことを実現する方法です。

「Zはキャッシュするには少し大きい」ということを完全には理解していません。プログラムの実行中に Z の値が多すぎてメモ化が役に立たないことを意味する場合は、 f(Z) のすべての値を事前に計算し、実行時にそれらを調べることに頼る必要があるかもしれません。これができない場合 (プログラムが遭遇する Z の値がわからないため)、メモ化に戻ります。それでも遅すぎる場合、唯一の現実的な選択肢は、Python よりも高速なものを使用することです (Psyco、Cython、ShedSkin、または手動でコーディングされた C モジュールを試してください)。

于 2011-03-14T07:12:24.107 に答える
0

このシナリオでラムダを使用しない理由に興味がありますか?

f = lambda: g(z)
if x:
    return 5
if y:
    return f()
if z:
    return h(f())
于 2022-02-04T17:23:10.303 に答える