2013-01-19 19 views
9

の効率化の可能性の重複:
Python FAQ: “How fast are exceptions?”Pythonの - キャッチ例外

私はPythonは例外に関して「許可を求めるするよりも許しを求めることより良い」哲学を実装していることを読んで覚えています。作者によると、これはPythonコードが例外を引き起こすようなことをしようとしているかどうかを事前に判断するのではなく、多くのtry - except節を使うべきだということを意味していました。

私はWebアプリケーションでいくつかtry-except句を書きましたが、コードが実行されるほとんどの時間は例外が発生します。したがって、この場合、例外を発生させてキャッチすることが標準になります。これは効率的な観点から悪いですか?私はまた、育った例外をキャッチすることは大きなパフォーマンスオーバーヘッドを持っていると私に言う人を覚えています。

try-except句を使用すると、例外が発生してほとんどすべての時間が奪取されると予想されるので、非効率的なのでしょうか?

これは、Django ORMを使用して、ユーザーをさまざまな第三者のソーシャルプロバイダに関連付けるオブジェクトをチェックするコードです。

try: 
    fb_social_auth = UserSocialAuth.objects.get(user=self, provider='facebook') 
    user_dict['facebook_id'] = fb_social_auth.uid 
except ObjectDoesNotExist: 
    user_dict['facebook_id'] = None 

try: 
    fs_social_auth = UserSocialAuth.objects.get(user=self, provider='foursquare') 
    user_dict['foursquare_id'] = fs_social_auth.uid 
except ObjectDoesNotExist: 
    user_dict['foursquare_id'] = None 

try: 
    tw_social_auth = UserSocialAuth.objects.get(user=self, provider='twitter') 
    user_dict['twitter_id'] = tw_social_auth.uid 
except ObjectDoesNotExist: 
    user_dict['twitter_id'] = None 

今私たちがサイトに参加するために、新しいユーザーのための主な方法として、「Facebookのアカウントでログインし、」強制されているので、最初のものはめったに、例外を負いません。しかし、TwitterやFoursquareは、友達やフォロワーをインポートしたい場合に備えてオプションです。ほとんどの人がそうしないと思います。

私はこのロジックをより良い方法でコーディングすることができます。

+1

コードの具体例は何ですか? –

+1

「許諾を求めるよりも許してもらう」を適用することは、どこでも当てはまりますが、確かにベスト・アイデアではありません。これを考えてみましょう: 'try..catch'内のコードへの呼び出しの95%で例外が発生しているとわかったら、それを書き直してください。 –

+1

「例外は例外的な場合です」ということです。ほとんどの場合、例外が予想される場合は、間違った方法で例外を使用している可能性があります。 –

答えて

6

パフォーマンス、可読性、正確性、拡張性、保守性などの懸念があるコードを書くときはいつでも、 残念ながら、これらの各方向のコードを同時に改善することはできません。速いものは、例えば読みやすいものではないかもしれません。

try..exceptがPythonで推奨される理由の1つは、コードが使用される可能性があるすべての方法を予測できないためです。何らかのクラスのエラーが発生する可能性があります。したがって、try..exceptはコードをより再利用可能にする可能性があります。

しかし、except節に頻繁に到達すると、try..exceptが遅いことも当てはまります。

例外が発生していないようにブロックをコーディングして、try..exceptを使用して頻度の低い条件をキャッチする方法はありますか?

効率的に使用しない場合は、try..exceptを使用しないことを選択できます。プログラミングには厳しいルールはほとんどありません。懸念のバランスに基づいて自分の道を選ぶ必要があります。

+0

+1 forプログラミングには厳しい規則と厳しい規則がほとんどない。 :) –

+0

IMOの+1スポット。 – jldupont

2

速度を上げるためにこの機能を最適化しようとしている場合、実際のボトルネックになる可能性のあるものに焦点を当てる必要があります。オペレーティングシステムのコンテキスト切り替えを引き起こすそれぞれの3つのデータベースクエリは、例外をキャッチするよりもかなり長い時間がかかります。できるだけ速くコードを作成したい場合は、まず3つのデータベースクエリをすべて1つに結合します。

auth_objects = UserSocialAuth.objects.filter(user=self, provider__in=('facebook', 'foursquare', 'twitter')) 

このオブジェクトをループします。これらの3つのプロバイダがデータベース内の唯一のプロバイダである場合は、provider__inフィルタが不要な場合があります。

2

例外をキャッチするのは適度に高価です(いくつかのタイミングについては以下を参照してください)、プログラムのボトルネックには望みませんが、例外をキャッチする例はあります実行時間のごく一部であり、Model.objects.getへの呼び出しと比較して、SQLクエリを構築し、それをデータベースサーバに送信し、データベースがそのようなオブジェクトがないことを報告するまで待つ必要があります。

いくつかのタイミングの例。関数f2は例外をスローしてキャッチしますが、f1は例外を使用せずに同じ機能を実装します。そう(

def f3(): 
    try: 
     MyModel.objects.get(id=999999) 
    except MyModel.DoesNotExist: 
     pass 

これはf2比べて約400倍の時間がかかります:

d = dict() 

def f1(): 
    if 0 in d: return d[0] 
    else: return None 

def f2(): 
    try: return d[0] 
    except KeyError: return None 

>>> timeit(f1) 
0.25134801864624023 
>>> timeit(f2) 
2.4589600563049316 

そしてf3は、DjangoのORMを経由して(同じマシン上で実行されている)データベースから、存在しないオブジェクトを取得しようとします私はデフォルトのnumber=1000000の反復が完了するのを待っていないと思っていました)。

>>> timeit(f3, number=1000) 
1.0703678131103516 
関連する問題