2

関数オブジェクトを辞書キーとして使用しています。これらの関数の結果をキャッシュする必要があるため、これを行っています。これは大まかに私が使用しているコードです:

# module cache.py:
calculation_cache = {}
def cached(func):
  # func takes input as argument
  def new_function(input):
    try:
      result = calculation_cache[(func, input)]
    except KeyError:
      result = func(input)
      calculation_cache[(func, input)] = result
    return result
  return new_function

# module stuff.py
@cached
def func(s):
  # do something time-consuming to s
  # ...
  return result

func.__module__ + func.__name__の代わりに使用することもできますがfuncfunc問題なく動作する場合は、名前の競合が発生する可能性があるため、むしろ使用したいと思います (たとえば、lambda関数、ネストされた関数、または同じ名前の別の関数に置き換えられた関数など)。

これは問題なく動作するように見えますが、テストが難しい状況で問題が発生する可能性があると思われます。

たとえば、何らかの理由で関数が削除され、別の関数がそのメモリ空間を再利用していることを懸念しています。この場合、私のキャッシュは無効になりますが、それは認識されません。これは正当な懸念事項ですか?もしそうなら、それに対処する方法はありますか?

関数を削除できますか? モジュールをリロードすると、関数が新しいアドレスに移動しますか (したがって、そのハッシュ値が変更され、新しい関数のために古いメモリ アドレスが解放されます)? 誰かが (何らかの奇妙な理由で) モジュールで定義された関数を単純に削除することはできますか?

で明示的に定義された関数でのみこれを行うことが安全である場合はdef、例外をデコレータとして使用することを禁止できますcached(強制する方法はわかりませんが、cacheddocstring で文書化することはできます)。

4

2 に答える 2

3

上記の懸念事項のすべてに対応できるかどうかはわかりませんが、そのうちの 1 つには対応できます --

関数をガベージ コレクションできない理由がわかりません。ただし、関数はディクショナリのキーであるため、そのディクショナリが存在する限り、関数の参照カウントがゼロになることはなく、ガベージ コレクションの対象にもなりません。

モジュールのリロードについてはわかりませんが、これは本当に心配する必要のない、まれなケースのようです。モジュールは実際にはリロードすることを意図していません...状況によってはそれを行うことができるという事実は、主に対話型ターミナルでのデバッグ目的であり、実際のコードで使用することを意図したものではありません...(私が知る限りとりあえず ...)

于 2012-09-07T04:30:47.537 に答える
1

コードが機能するはずです。関数オブジェクトはまだdictによって参照されているため、他の人が言ったように、ガベージコレクションは取得されません。関数を削除するか、モジュールを再ロードすることができます。これは、funcラベルが関数オブジェクトの新しいバージョンを参照する (または何も参照しない) ことを意味します。しかし、古い関数オブジェクトは、 によって参照されなくなってもメモリ内に残りますfunc。ディクショナリには、古い関数と新しい関数の個別のエントリが含まれます。これは、古い関数に添付された古い結果が得られないことを意味するため、より適切に機能する可能性があります。辞書をバックドアとして使用して、古いバージョンの関数を取得することもできます。Calculation_cache.keys() を列挙するだけで元に戻ります。

于 2012-09-07T04:53:41.270 に答える