那个设定
我经常很难确定何时以及如何使用异常。让我们考虑一个简单的示例:假设我正在抓取一个网页,说“ http://www.abevigoda.com/ ”,以确定Abe Vigoda是否还活着。为此,我们要做的就是下载页面并寻找出现“ 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
然后是空列表。 - 该网页已被更改,可能使我们对该页面的假设错误。也许我们希望提到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:介于两者之间
我认为第二种方法(带异常)是可取的,但是我不确定是否在其中正确使用了异常。我很好奇看到更有经验的程序员将如何处理这个问题。