2009-03-17 27 views
2

私は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で使用しようとしていますか?

+0

あなたは* why *;単体テストの代わりにこれがなぜ必要なのかは明らかではありません。 - また、痛みのような目的音で機能が変化したときに.frzファイルを更新する。 – Deestan

+0

"一般的に、このような機能を使用しますか?"決して。申し訳ありませんが、この説明は私にはほとんど意味がありません。ユースケースはあいまいです。関数の結果をファイルに保存するだけですか? –

+0

コメントありがとうございました!私はメインポストで答えさせてください。 – fulmicoton

答えて

3

"一般的に、そのような機能を使用して制限を知っていますか?"

率直に言えば - 決してありません。

このようにして関数の結果を「フリーズ」する状況はありません。

ユースケースは2つの間違ったアイデアに基づいているように見えます。(1)単体テストはハードか複雑か高価です。 (2)コードを記述し、結果を「フリーズ」し、リファクタリングのためにフリーズした結果を何らかの形で使用する方が簡単かもしれません。これは役に立たない。確かに、間違った答えを凍結する本当の可能性は、これを悪い考えです。

まず、「整合性対正しさ」について説明します。これは複雑なデコレータセットよりも簡単なマッピングで保存する方が簡単です。

フリーズデコレータを書き込む代わりにこれを行います。

print "frozen_f=", dict((i,f(i)) for i in range(100)) 

作成された辞書オブジェクトは、フリーズした結果セットとして完全に機能します。デコレータなし。話す複雑さはありません。

第2に、「ユニットテスト」です。

ユニットテストのポイントはではありません。は、いくつかのランダムな結果を「フリーズ」します。単体テストのポイントは、実際の結果を比較し、結果が別のもの(より単純で、より明白で、性能の低い方法)で開発されることです。通常、単体テストは手で開発した結果を比較します。ユニットテストでは、明白ではあるが恐ろしく遅いアルゴリズムを使用して、いくつかの重要な結果が得られる場合もあります。

テストデータの周りの点は、「フリーズした」結果ではありません。テストデータを持つ点は、それが独立した結果であるということです。機能が動作することを確認するために、別の人によって(時には異なる人によって)異なって行われます。

申し訳ありません。これは私にとっては悪い考えです。ユニットテストの意図を覆すように見えます。


を「しかし、誰もテストケースを足すのオーバーヘッドを否定することはできません」実は、多くの人々は、「オーバーヘッド」を否定します。無駄な時間と労力の意味で「オーバーヘッド」ではありません。私たちの中には、ユニットテストが不可欠です。それらがなければ、コードがうまくいくかもしれませんが、偶然によってのみです。彼らと一緒に、私たちはそれが実際に働くという十分な証拠を持っています。それが動作する特定のケース。

+0

ありがとうございます。 >単体テストのポイントは、実際の結果を比較して、別の結果が得られることです(よりシンプルで、はっきりしない、パフォーマンスの低い方法)。 仕様が部分的に従来の単純関数の単体テストはどうでしょうか?例: 悪質な引数の場合にはNoneを返すか返す – fulmicoton

+0

単体テスト(arg検証など)の場合、あなたは*手動で答えを出しています。結果を手動で開発するのは本当に簡単です。しかし、予想される結果を手動で開発するのと同じ基本パターンです。 –

0

あなたは不変条件または事後条件を実装するために探していますか?

結果を明示的に指定する必要があります。これは、ほとんどの問題を取り除くためです。

+0

>インバリアントやポストコンディションを実装しようとしていますか? これは実際には...これは、あなたの関数が1年で同じように動作するかどうかを確認することです。しかしそれは良い考えです。私はすでに別のプロジェクトでそれを使用しています。 – fulmicoton

関連する問題