2016-04-04 22 views
6

私は最適化したい。Pythonでループする:最初の繰り返しの前に行うこと

シンプルなソリューション

connection = get_db_connection() 
for item in my_iterator: 
    push_item_to_db(item, connection) 

欠点:

get_db_connection()が遅いです。 my_iteratorが空の場合、私はそれを呼ぶことを避けたいと思います。

ソリューション

connection = None 
for item in my_iterator: 
    if connection is None: 
     connection = get_db_connection() 
    push_item_to_db(item, connection) 

欠点 "なしの場合":

100kの項目がmy_iteratorである場合(それは一度だけ必要とされているが)、その後、if connection is Noneが100K回呼び出されます。私はこれを避けたい。

完璧なソリューション...

  1. は、イテレータが
  2. 空の場合は、すべての反復のために無駄if connection is None:を呼び出すことはありませんget_db_connection()を呼び出すことはありません。

connection = None 
for item in my_iterator: 
    if connection is None: 
     connection = get_db_connection() 
    push_item_to_db(item, connection) 

シンプルなソリューション:

+4

これはオーバー最適化大規模です。 '' not i''は、 '' push_item_to_db''で何が起きるかと比べると、あまり重要でないオーバーヘッドです。 –

+0

'get_db_connection'が遅い場合、' if'文を避けるために「最適化」するのは正しいことではありません...それはあなたのイテレータが各ループに対して終了する 'StopIteration'を投げるべきです空です。 –

+0

@DanielRosemanはい、これは「大過剰の最適化」です。しかし、それにもかかわらず私はそれを解決する手がかりがないので、私はこの質問が好きです。私のためには本当に私を "傷つける"質問がもっと楽しいです。 – guettli

答えて

5

あなたのような何かを行うことができます。それを考え直す必要はありません。 100Kの操作でも、x is Noneは、Pythonのオペコードを1つ取るだけの参照比較です。すべての挿入時に発生する完全なtcpラウンドトリップ+ディスク書き込みと比較して、これを最適化する必要はありません。それはおそらく本当である

def put_items_to_database (iterator): 
    try: 
     item = next(iterator) 

     # We connect to the database only after we 
     # know there at least one element in the collection    
     connection = get_db_connection() 

     while True: 
      push_item_to_db(item, connection) 
      item = next(iterator) 
    except StopIteration: 
     pass 

+0

はい、あなたの「if」は私より速いです。それにもかかわらず私はそれを避けたいです。 – guettli

+1

それからあなたは@ coredumpの答えに行かなければならないでしょう。これは明示的な 'if'を避ける唯一の方法です。最適化することはまだ保証されていないことに留意してください。実際には遅くなる可能性があります。私があなたのコードを見ていれば、もっと簡単な解決策を検討することをお勧めします。 – viraptor

+0

他に解決策はないと思いますか? – guettli

-1

あなたは私は、Pythonの専門家ではないですコード

if (len(my_iterator)>0): 
    connection = get_db_connection() 
    for item in my_iterator: 
     push_item_to_db(item, connection) 
+1

イテレータの長さは一般的には提供されません。 – khelwood

2

の全体のセクションの前にイテレータ・カウントをチェックすることができますが、私はこのような何かをするだろうパフォーマンスはここでデータベースに結び付けられています。しかし、疑問は、不必要な作業を避ける方法を見つけることであり、上記は反復中に何が起こるかを正確に制御する基本的な方法です。

他の解決策は何らかの形で「よりシンプル」ですが、一方で私はこれがより明白であり、最小の驚きの原則に従うと考えています。

+0

はい、これは正解のようです。ありがとうございました。 – guettli

1

ソリューションは、1

これはwhile Trueループなしで動作します。

try: 
    next(my_iterator) 
    connection = get_db_connection() 
    push_item_to_db(item, connection) 
except StopIteration: 
    pass 
for item in my_iterator: 
    push_item_to_db(item, connection) 

ソリューション2

あなたはそのイテレータがNone(またはその他のユニークなオブジェクト)を返すことはありませんことがわかっている場合、あなたはnext()のデフォルトを利用することができます:

if next(my_iterator, None) is not None: 
    connection = get_db_connection() 
    push_item_to_db(item, connection) 
for item in my_iterator: 
    push_item_to_db(item, connection) 

ソリューション3

イテレータによって返されない値を保証できない場合は、センチネルを使用できます。

sentinel = object() 
if next(my_iterator, sentinel) is not sentinel: 
    connection = get_db_connection() 
    push_item_to_db(item, connection) 
for item in my_iterator: 
    push_item_to_db(item, connection) 

ソリューションitertools.chain()を使用して4

from itertools import chain 

for first_item in my_iterator: 
    connection = get_db_connection() 
    for item in chain([first_item], my_iterator): 
     push_item_to_db(item, connection) 
+0

"while True"ループの何が問題なのですか?私は本当に好奇心を持っています。なぜなら、あなたがあなたのすべてのソリューションで「push_item_to_db」を2回呼び出すのを好むように思われるからです。 – coredump

+1

何も本当に間違っています。可能であれば、 'while'ループよりも' for'ループを好むだけです。 「感じる」は、よりやさしい(あるいは多分))。 –

2
for item in my_iterator: 
    # First item (if any) 
    connection = get_db_connection() 
    push_item_to_db(item, connection) 
    for item in my_iterator: 
     # Next items 
     push_item_to_db(item, connection) 
+0

唯一の欠点は、それは(私にとってそれはうまくいく)いくつかの人々によって "あまりにも賢い"とラベルを付けることができたということです。 – coredump

+0

@coredump:私が見ている欠点(他のいくつかの解決策でも)は、ループの本体を複製する必要があるということです。 –

+0

はい、私はこれについて他のところでコメントしましたが、何とかあなたの答えに気付かなかったのです。それでも、私はこれがより簡単に見えるので、これを好む。 – coredump

関連する問題