2017-03-11 19 views
0

私はexecとevalはほとんどの場合お勧めできませんが、変数数のステートメントで関数を動的に作成したいと思います。これを行う理由は、1つの文で3つの関数を生成すると、3つの文で1つの関数よりパフォーマンスが悪くなるためです。可変数の文で動的に関数を作成する

基本的にはいくつかのオブジェクトがあり、入力からフィールドとオブジェクトを読み込みます。

さまざまな量のobjに対して、多くの手動機能を作成するのではなく、1つの関数を組み立てたいと思っています。 (make_funcNを生成する方法はありますかある?) を回避する方法はあります

def make_func1(obj1, field1, obj2, field2): 
    def copy(): 
    obj1.__setattr__(field1, obj2.__getattribute__(field2)) 
    return copy 

def make_func2(obj1, field1, obj2, field2, \ 
       obj3, field3, obj4, field4): 
    def copy(): 
    obj1.__setattr__(field1, obj2.__getattribute__(field2)) 
    obj3.__setattr__(field3, obj4.__getattribute__(field4)) 
    return copy 

def make_func3(...): 
    ... 

おかげで、

+0

"1つのステートメントで3つの関数を生成すると、それぞれが3つのステートメントを持つ1つの関数よりもパフォーマンスが劣ります"という記述が真である可能性があります。しかし、この機能を使用しない限り、特にネイティブマクロをサポートしていない言語でこれを行う際の複雑さとのバランスが取れていても、パフォーマンスの差は問題にはなりません。 –

+0

はい私はこの機能を非常に重く使用しています.... –

+0

もう一度ベンチマークを行ってください。パフォーマンスが重要な場合、データの最適化ベースは、より高速である可能性を推測する上で非常に優れています。特に、最適化されたバージョンがメタプログラミングに依存し、独自の関数呼び出しを含む場合。 –

答えて

0

これはOPが要求したものではありません。主にarityで異なる一連の同様の属性コピー機能を作成するのではなく、いくつかの簡単な機能とこの問題の「帰無仮説」を提示します。複数の類似機能を作成することは善意ですが、状況。

あなたが設定するフィールドを持っているとして、最も簡単な方法は、単一のコピー機能になり、何回も繰り返し処理:

def copyattr(obj1, field1, obj2, field2): 
    setattr(obj1, field1, getattr(obj2, field2)) 

コールあなたが好きなすべての属性を設定するために、必要に応じて何度でもこと。 シンプルでPythonで、私が実行したベンチマークに基づいて非常にうまく動作します。

あなたの代わりに、その後、コピー命令(あなたの拡張引数リストが提案する)のフラットなリストが必要な場合:

def copyattrs(*args): 
    for i in range(0, len(args), 4): 
     setattr(args[i], args[i+1], getattr(args[i+2], args[i+3])) 

は、トリックを行います。これは、以前と同じ順序で、任意に長いフラットシーケンスのパラメータを取ります。 copy()埋め込みループを持つ関数を返す答えのいくつかは、セットアップステップ(make_func())を必要とし、クロージャを介してパラメータを送信することを除いて、基本的にこれです。

あなただけのPython 3を使用している場合は、構文を開梱拡張引数で数ナノ秒を剃ることができます。

def copyattrs(*args): 
    for i in range(0, len(args), 4): 
     setattr(*args[i:i+2], getattr(*args[i+2:i+4])) 

しかし、私が試した何のバリアント - 特定アリティのコピー機能を生成含む - はあまり行われていませんこの作業の違い。 Pythonのデータ処理のセマンティクスに基づいて、これらのアプローチはどれも多くのデータをコピーしません。それらはすべて大きなデータセットではなくポインタをコピーします。それはあなたがそれを行うどのような方法でも非常に効率的です。その努力の多くは、未加工のデータコピーよりも、パラメータの構築とアクセス、および関数呼び出しのセットアップ/ティアダウンで消費されます。私は膨大な数のコピー(1,000,000〜2,000,000,000)を実行しなければならず、興味深い時間を取ることさえ始めることさえありました。 200,000,000の属性コピーの完全なベンチマーク実行でさえ数秒しかかかりませんでした。また、毎回テスト状態をゼロにリセットするために必要な完全なデータセットのコピーが含まれていました。

この「動的属性コピー機能を作成する」アプローチでは、短所はほとんどないようです。これは便利な実験ですが、実行したベンチマークのいずれも、特にカスタム属性コピー機能を作成し、その後引数を取り込むのに必要な時間を考慮に入れると、はるかに高速な結果が得られませんでした。どのようにそれらが使用されるかに応じて、メモリ内に多数のクロージャを生成して保持するための潜在的な物理的オーバーヘッドもあります。私が実行したシナリオでは、クロージャ駆動のパラメータなしcopy()が実際に最悪のベンチマークをしました。

著名なコンピュータ科学者CAR HoareとDonald Knuthは、「時期尚早最適化はすべての悪の根源です」と主張しています。したがって、魅力的な最適化に頼る前に、実際のパフォーマンスの問題がどこにあるのかを知り、提案された解決策が実際に改善されていることを確認してください。 Ned Batchelder nails this

… "あなたはどこに問題があるかわからない。"私は、何回スピードをあげて試してみたか、それがうまくいかなかったかどうかについて気の利いたアイデアを何度も覚えていません。

彼が言及している記事、Russ OlsenのFive Truths about Optimizationは、物事をスピードアップするための確固たる読書と良い指針です。卓越した長さのハウツーについては、Jon Bentleyの効率的なプログラムの作成を試してみてください。

+0

これらのリストベースのアプローチを比較していただきありがとうございます。あなたが言ったように、彼らはあまり差がありません。私が最初に意味していたことは、実際には "範囲(0、len(args)、4)のために"ループを持たない関数を生成することです。 "代わりに、私はこれを各機能の2つのステートメントにまとめると想像しました。私は実験しましたが、これは実際に3つの関数を1つの関数で3つの変数をコピーするよりも速いです。しかし、この重要なタスクのためにPythonに頼らないと正しいかもしれません。 –

+0

ランダムに生成されたデータを使って数十億回の反復をテストしました。この種の[ループアンロール](https://en.wikipedia.org/wiki/Loop_unrolling)を行うときに違いがありましたか?はい。それは大きな勝利でしたか?あなたのプログラム全体をより速くするようなもの?いいえ、実際には、アンローリングの一部(クロージャーで使用された場合は、はるかに悪化しました)があります。しかし、いつものように、YMMV。 –

0
def make_func1(*args): 
    assert len(args) % 4 == 0 

    def copy(): 
     for i in range(0, len(args), 4): 
      obj1, field1, obj2, field2 = args[i:i + 4] 
      obj1.__setattr__(field1, obj2.__getattribute__(field2)) 

    return copy 
+0

@MosesKoledoyeありがとう、正しかったと思っていなかった、範囲を修正しました。 –

3

まず第一に、あなたが使用する必要がある組み込みgetattr、代わりに__setattr____getattribute__setattr。ミニチュア・パフォーマンスの向上を読みやすいように交換しないでください。

def make_func(*args): 
    if len(args) % 4 != 0: 
     raise ValueError("number of arguments must be a multiple of 4") 
    def copy(): 
     for i in range(0, len(args), 4): 
      setattr(args[i], args[i+1], getattr(args[i+2], args[i+3])) 
    return copy 
+0

私はオンラインで読むことができ、 '__getattribute__'は基本的にgetattrよりも少ない仕事をしています。私はtimeitを試してみたところ、 '__getattribute__'はgetattrより速いことがわかりました。 –

+0

これは組み込み関数が背後にあるものを呼び出すためですが、Pythonはマイクロ最適化に関するものではないことに注意してください。 **コードの可読性**は、言語によって強く強調されています。 –

0

あなただけのリストやタプルなどのオブジェクトやフィールドに渡すことができます:

その後、あなたはグループあなたargs四つんばいにしてから設定し、それに応じたオブジェクトの各ペアの属性を取得することができます

def make_func(to_objs, to_fields, from_objs, from_fields): 
    def copy(): 
     for to_obj, to_field, from_obj, from_field in zip(to_obj, to_fields, 
                  from_objs, from_fields): 
      to_obj.__setattr__(to_field, from_obj.__getattribute__(from_field)) 
    return copy 
関連する問題