私はPythonのフリーズデコレータを作成しようとしています。次のように単純なフリーズ動作デコレータ
考え方は次のとおりです。
私は間違っているかもしれないが、私は テストケースには2回の主な使用があると思います(2件のコメントを受けて)
。コードを書く前に 理想的には、開発者が作成している場合:
一つは、テスト駆動開発です。 この規律 は、開発前に実際のインターフェイスを定義する必要があるため、通常はアーキテクチャの定義に役立ちます。 場合によっては、 が開発者の間でジョブをディスパッチする人がテストケースを書いており、 がそれを念頭に置いて効率的に説明するために使用する場合もあります。 私はそのようなテストケースの使用経験はありません。
2番目のアイデアは、まともなサイズの のプロジェクトと複数のプログラマーが壊れたコードで苦しんでいるという考えです。 作業に使用するものが、無修正のリファクタリングのように見える変更 から壊れている可能性があります。 良いアーキテクチャですが、コンポーネント間のゆるいカップルは この現象と戦うのを助けるかもしれません; あなたがプログラムの動作を壊すことがないことを確認するためにいくつかのテストケースを書いている場合は、 をよく眠ります。
しかし、 誰もテストケースを足すのオーバーヘッドを否定することはできません。 最初のケースでは、テストケースが実際に の開発を指導していると主張することができ、したがって、オーバーヘッドとして考えるべきではありません。
は率直に言って、私はかなり若いプログラマだと私は あなただったら、このテーマに関する私の言葉は本当に貴重ではありません... はとにかく、私はのMOST会社/プロジェクトは、そのような を働いていないと思い、そのユニットテストは、主に...二 場合には言い換えれば
を使用ではなく、プログラムが正しく 動作していることを確実にしていると、それが 将来的には同じように動作することを確認を目指しています。
この凍結デコレータを使用して、このテストの書き込みコストなしでこのニーズを満たすことができます。
はのは、それは完全にいいです、あなたが最適化されたバージョンとしてそれを書き換えたいあなたは機能
def pow(n,k):
if n == 0: return 1
else: return n * pow(n,k-1)
があるとしましょう。 大きなプロジェクトの一部です。あなたはそれが同じ結果を返すことを望みます いくつかの値のために。 テストケースの苦痛を経験するのではなく、 種類の凍結デコレータを使用することができます。
デコレータを実行する初めて、 デコレータ(0未満、7)定義引数で関数を実行 ような何かとマップ(Fに結果を保存 - >引数 - >結果)プログラムが実行される
@freeze(2,0)
@freeze(1,3)
@freeze(3,5)
@freeze(0,0)
def pow(n,k):
if n == 0: return 1
else: return n * pow(n,k-1)
次回は、デコレータは、このマップをロードし、これらの引数は、この関数の結果を変更しないように をチェックします。私はすでに
from __future__ import with_statement
from collections import defaultdict
from types import GeneratorType
import cPickle
def __id_from_function(f):
return ".".join([f.__module__, f.__name__])
def generator_firsts(g, N=100):
try:
if N==0:
return []
else:
return [g.next()] + generator_firsts(g, N-1)
except StopIteration :
return []
def __post_process(v):
specialized_postprocess = [
(GeneratorType, generator_firsts),
(Exception, str),
]
try:
val_mro = v.__class__.mro()
for (ancestor, specialized) in specialized_postprocess:
if ancestor in val_mro:
return specialized(v)
raise ""
except:
print "Cannot accept this as a value"
return None
def __eval_function(f):
def aux(args, kargs):
try:
return (True, __post_process(f(*args, **kargs)))
except Exception, e:
return (False, __post_process(e))
return aux
def __compare_behavior(f, past_records):
for (args, kargs, result) in past_records:
assert __eval_function(f)(args,kargs) == result
def __record_behavior(f, past_records, args, kargs):
registered_args = [ (a, k) for (a, k, r) in past_records ]
if (args, kargs) not in registered_args:
res = __eval_function(f)(args, kargs)
past_records.append((args, kargs, res))
def __open_frz():
try:
with open(".frz", "r") as __open_frz:
return cPickle.load(__open_frz)
except:
return defaultdict(list)
def __save_frz(past_records):
with open(".frz", "w") as __open_frz:
return cPickle.dump(past_records, __open_frz)
def freeze_behavior(*args, **kvargs):
def freeze_decorator(f):
past_records = __open_frz()
f_id = __id_from_function(f)
f_past_records = past_records[f_id]
__compare_behavior(f, f_past_records)
__record_behavior(f, f_past_records, args, kvargs)
__save_frz(past_records)
return f
return freeze_decorator
はダンプと結果の比較...(下記参照)迅速デコレータを書いたが、私はあなたのアドバイスを必要とする に関するいくつかの問題を傷つける
はすべてのために簡単ではありませんタイプ。今、私はこの問題を解決するために、関数を使うことを考えています(私はそれを後処理と呼んでいます)。 resを保存するのではなく、res1を保存する代わりに、res1 res2を比較するのではなく、postprocess(res1)== postprocess(res2)を比較します。 ユーザが定義済みの後処理機能をオーバーロードさせることが重要です。 私の最初の質問は: オブジェクトがダンプ可能かどうかを確認する方法を知っていますか?
装飾された機能のキーを定義することは苦痛です。次のスニペットでは、 私は汎用モジュールとその名前を使用しています。 **あなたはそれをよりスマートな方法で考えることができますか? **
以下のスニペットは動作しますが、テストや録音時にファイルを開いたり閉じたりします。これは単なる愚かなプロトタイプです...しかし、あなたはファイルを開くための良い方法を知っていますか?すべての機能のためにデコレータを処理し、ファイルを閉じてください...
私はこれにいくつかの機能を追加するつもりです。例えば、 を定義して、引数のセットをブラウズしたり、実際の使用から引数を記録したりすることを定義する可能性を追加してください。 なぜこのようなデコレータから期待しますか?
一般的に、その制限を知っているこのような機能を使用しますか...特にPOOで使用しようとしていますか?
あなたは* why *;単体テストの代わりにこれがなぜ必要なのかは明らかではありません。 - また、痛みのような目的音で機能が変化したときに.frzファイルを更新する。 – Deestan
"一般的に、このような機能を使用しますか?"決して。申し訳ありませんが、この説明は私にはほとんど意味がありません。ユースケースはあいまいです。関数の結果をファイルに保存するだけですか? –
コメントありがとうございました!私はメインポストで答えさせてください。 – fulmicoton