設定
例外をいつどのように使用するかを判断するのに苦労することがよくあります。簡単な例を考えてみましょう。AbeVigodaがまだ生きているかどうかを判断するために、「http://www.abevigoda.com/」と言うWebページをスクレイピングしているとします。これを行うには、ページをダウンロードし、「Abe Vigoda」というフレーズが表示される時間を探すだけです。安倍のステータスが含まれているため、最初の外観を返します。概念的には、次のようになります。
def get_abe_status(url):
# download the page
page = download_page(url)
# get all mentions of Abe Vigoda
hits = page.find_all_mentions("Abe Vigoda")
# parse the first hit for his status
status = parse_abe_status(hits[0])
# he's either alive or dead
return status == "alive"
where parse_abe_status(s)
は「Abe Vigoda is something」という形式の文字列を取り、「something」部分を返します。
このページを安倍のステータスのためにスクレイピングするはるかに優れた、より堅牢な方法があることを議論する前に、これは私がいる一般的な状況を強調するために使用される単純で不自然な例であることを忘れないでください。
さて、このコードはどこで問題に遭遇しますか?他のエラーの中でも、いくつかの「予期される」エラーは次のとおりです。
download_page
ページをダウンロードできない可能性があり、をスローしIOError
ます。- URLが正しいページを指していないか、ページが正しくダウンロードされていないため、ヒットはありません。
hits
空のリストです。 - Webページが変更されたため、ページに関する想定が間違っている可能性があります。Abe Vigodaについて4件の言及があると予想されますが、5件が見つかりました。
- 何らかの理由で、
hits[0]
「Abe Vigoda is something」という形式の文字列ではない可能性があるため、正しく解析できません。
最初のケースは、実際には私にとって問題ではありませんIOError
。それでは、他のケースと、それらをどのように処理するかを考えてみましょう。しかし、まず、parse_abe_status
可能な限り愚かな方法で実装すると仮定しましょう。
def parse_abe_status(s):
return s[13:]
つまり、エラーチェックを行いません。次に、オプションについて説明します。
オプション1:返品 None
呼び出し元に何かが間違っていることを返すには、次のようにしますNone
。
def get_abe_status(url):
# download the page
page = download_page(url)
# get all mentions of Abe Vigoda
hits = page.find_all_mentions("Abe Vigoda")
if not hits:
return None
# parse the first hit for his status
status = parse_abe_status(hits[0])
# he's either alive or dead
return status == "alive"
呼び出し側が受信した場合None
、私の機能から、彼は決してエイブ・ヴィゴダの言及、およびようがなかったと仮定しなければならない何かが間違っていました。しかし、これはかなり曖昧ですよね?そして、それがhits[0]
我々が思っていたものではない場合には役に立ちません。
一方、いくつかの例外を入れることができます。
オプション2:例外の使用
hits
が空の場合IndexError
、を試みるとがスローされhits[0]
ます。しかし、呼び出し元はIndexError
、私の関数によってスローされたものを処理することを期待されるべきではありませんIndexError
。find_all_mentions
彼が知っているすべてのために、それによって投げられたかもしれません。そのため、これを処理するカスタム例外クラスを作成します。
class NotFoundError(Exception):
"""Throw this when something can't be found on a page."""
def get_abe_status(url):
# download the page
page = download_page(url)
# get all mentions of Abe Vigoda
hits = page.find_all_mentions("Abe Vigoda")
try:
hits[0]
except IndexError:
raise NotFoundError("No mentions found.")
# parse the first hit for his status
status = parse_abe_status(hits[0])
# he's either alive or dead
return status == "alive"
では、ページが変更され、予想外の数のヒットがあった場合はどうなりますか?コードはまだ機能する可能性があるため、これは壊滅的ではありませんが、呼び出し元はさらに注意を払うか、警告を記録することができます。だから私は警告を投げます:
class NotFoundError(Exception):
"""Throw this when something can't be found on a page."""
def get_abe_status(url):
# download the page
page = download_page(url)
# get all mentions of Abe Vigoda
hits = page.find_all_mentions("Abe Vigoda")
try:
hits[0]
except IndexError:
raise NotFoundError("No mentions found.")
# say we expect four hits...
if len(hits) != 4:
raise Warning("An unexpected number of hits.")
logger.warning("An unexpected number of hits.")
# parse the first hit for his status
status = parse_abe_status(hits[0])
# he's either alive or dead
return status == "alive"
最後に、私たちはそれstatus
が生きているか死んでいないかを見つけるかもしれません。たぶん、何らかの奇妙な理由で、今日それが判明したcomatose
。それから、私は戻りたくないFalse
、それは安倍が死んだことを意味するからだ。ここで何をすべきですか?おそらく例外をスローします。しかし、どのような?カスタム例外クラスを作成する必要がありますか?
class NotFoundError(Exception):
"""Throw this when something can't be found on a page."""
def get_abe_status(url):
# download the page
page = download_page(url)
# get all mentions of Abe Vigoda
hits = page.find_all_mentions("Abe Vigoda")
try:
hits[0]
except IndexError:
raise NotFoundError("No mentions found.")
# say we expect four hits...
if len(hits) != 4:
raise Warning("An unexpected number of hits.")
logger.warning("An unexpected number of hits.")
# parse the first hit for his status
status = parse_abe_status(hits[0])
if status not in ['alive', 'dead']:
raise SomeTypeOfError("Status is an unexpected value.")
# he's either alive or dead
return status == "alive"
オプション3:中間
例外を伴う2番目の方法が望ましいと思いますが、その中で例外を正しく使用しているかどうかはわかりません。より経験豊富なプログラマがこれをどのように処理するかを知りたいです。