为了消除python模块中潜在的竞争条件,我写了一些专门的工作流程,我学会了python的“更容易请求宽恕而非许可”(EAFP)编码风格,我现在提出了很多自定义异常使用try / except块,我曾经使用if / thens。
我是python的新手,这种EAFP风格在逻辑上很有意义,似乎让我的代码更加健壮,但有些事情让人觉得有些过分。为每种方法定义一个或多个异常是不好的做法吗?
这些自定义异常往往只对单个方法有用,虽然它感觉像是一个功能正确的解决方案,但似乎需要维护很多代码。
这里有一个示例方法,例如:
class UploadTimeoutFileMissing(Exception):
def __init__(self, value):
self.parameter = value
def __str__(self):
return repr(self.parameter)
class UploadTimeoutTooSlow(Exception):
def __init__(self, value):
self.parameter = value
def __str__(self):
return repr(self.parameter)
def check_upload(file, timeout_seconds, max_age_seconds, min_age_seconds):
timeout = time.time() + timeout_seconds
## Check until file found or timeout
while (time.time() < timeout):
time.sleep(5)
try:
filetime = os.path.getmtime(file)
filesize = os.path.getsize(file)
except OSError:
print "File not found %s" % file
continue
fileage = time.time() - filetime
## Make sure file isn't pre-existing
if fileage > max_age_seconds:
print "File too old %s" % file
continue
## Make sure file isn't still uploading
elif fileage <= min_age_seconds:
print "File too new %s" % file
continue
return(filetime, filesize)
## Timeout
try:
filetime
filesize
raise UploadTimeoutTooSlow("File still uploading")
except NameError:
raise UploadTimeoutFileMissing("File not sent")
答案 0 :(得分:6)
为每个方法定义一个或多个异常
如果您的意思是每个方法实际定义了异常,如“在方法体内”,那么是。这是不好的做法。如果您定义两个与同一错误相关的异常,但是您创建了两个异常,因为两个不同的方法会引发它们,这也是如此。
如果你问每个方法提高多个例外是不好的做法,那么没有,这是一个好习惯。如果错误不是同一类别,那么为每个模块定义几个例外是完全可以的。
通常,对于较大的模块,您将定义多个异常。如果你要处理一些算术库,你会定义一个ZeroDivisionError和一个OverflowError(如果它们还没有在python中定义,因为你当然可以重用它们),那将是完全没问题的。
答案 1 :(得分:4)
为每种方法定义一个或多个异常是不好的做法吗?
是
每个模块一个更典型。当然,这取决于详细的语义。问题归结为:“你真正想要捕获什么?”
如果您在代码中永远不会使用except ThisVeryDetailedException:
,那么您的非常详细的例外情况就无济于事。
如果你可以这样做:except Error as e: if e.some_special_case
几次重要,那么你可以轻松地将每个模块简化为一个例外,并将你的特殊情况作为异常的属性而不是不同类型的异常处理。 / p>
常见建议(每个模块一个,名为Error
)意味着您的代码通常看起来像这样。
try:
something
except some_module.Error as e:
carry on
这为您提供了一个很好的命名约定:module.Error
。这涵盖了许多罪行。
在一个不相关的说明中,如果你认为你有“潜在的竞争条件”,你应该正确地重新设计或停止尝试使用线程或切换到多处理。如果你使用多重处理,你会发现很容易避免竞争条件。
答案 2 :(得分:4)
我会对此进行权衡,因为自定义异常对我来说很珍贵。我将解释我的情况,读者可以权衡他们自己的情况。
我是视觉效果公司的管道架构师 - 我所做的大部分工作都涉及开发我所谓的“Facility API” - 它是一个由许多模块组成的系统,可以处理从文件系统中查找内容,管理模块/工具/项目配置,用于处理来自各种CG应用程序的数据类型以实现协作。
我竭尽全力确保Python的内置异常永远不会冒出来。由于我们的开发人员将依赖现有模块的生态系统来构建他们自己的工具,因此让API通用IOError
转义会适得其反 - 特别是因为调用例程可能甚至不知道它正在读取文件系统(抽象是一件美丽的事情)。如果底层模块无法表达对该错误有意义的事情,则需要做更多的工作。
我解决此问题的方法是创建一个工具异常类,从中派生所有其他工具异常。对于特定类型的任务或特定主机应用程序,有一些子类 - 这允许我自定义错误处理(例如,Maya中引发的异常将启动UI以帮助进行故障排除,因为通常的异常将在不显眼的控制台中引发经常会被遗漏。)
各种报告都内置于工具异常类中 - 例外情况不会在没有内部报告的情况下显示给用户。对于一系列例外情况,我会在任何时候获得IM。其他人只是静静地报告到我可以查询最近(每日或每周)报告的数据库。每个链接到从用户会话捕获的EXTENSIVE数据 - 通常包括屏幕截图,堆栈跟踪,系统配置等等。这意味着我可以在报告问题之前对问题进行有效的故障排除 - 并且掌握的信息比大多数用户可能提供的信息更多。
不建议使用非常精细的渐变 - 例外接受传递的值(有时甚至是字典而不是字符串,如果我们想提供大量数据进行故障排除)以提供格式化的输出。
所以不 - 我不认为为每个模块定义一个或两个例外是不合理的 - 但它们需要有意义并为项目添加一些东西。如果您只是将IOError
包裹到raise MyIOError("I got an IO error!")
,那么您可能需要重新考虑。
答案 3 :(得分:1)
我认为不必为每种可能的情况都有一个非常具体的例外。一个UploadTimeoutError
可能会很好,你可以自定义异常字符串 - 毕竟这是字符串的用途。请注意python如何针对每种可能类型的语法错误都没有单独的异常,只是一般的SyntaxError
。
此外 - 实际上是否有必要为每个自定义例外定义__init__
和__str__
方法?据我所知,如果你没有实现任何异常行为,你不需要添加任何代码:
>>> class MyException(Exception): pass
...
>>> raise MyException("oops!")
Traceback (most recent call last):
File "<ipython console>", line 1, in <module>
MyException: oops!
>>> str(MyException("oops!"))
'oops!'