我在一个项目中工作,我们想验证一个参数是否可以在必要时作为异常引发。我们进行了以下操作:
def is_raisable(exception):
funcs = (isinstance, issubclass)
return any(f(exception, BaseException) for f in funcs)
这将处理以下满足我们需求的用例(目前):
^{pr2}$但是,对于旧样式的类,它失败了,因为旧版本(2.x)中可以对其进行提升。我们试着用这种方法解决这个问题:
^{3}$这通过了我们所有的测试,但是它不是很漂亮,而且如果我们考虑一些我们不希望用户通过的东西,但是可能会因为其他原因被扔进去,那么它仍然会让人觉得很不舒服。在
def test_is_raisable_exception(self):
"""Test that an exception is raisable."""
self.assertTrue(is_raisable(Exception))
def test_is_raisable_instance(self):
"""Test that an instance of an exception is raisable."""
self.assertTrue(is_raisable(Exception()))
def test_is_raisable_old_style_class(self):
"""Test that an old style class is raisable."""
class A: pass
self.assertTrue(is_raisable(A))
def test_is_raisable_old_style_class_instance(self):
"""Test that an old style class instance is raisable."""
class A: pass
self.assertTrue(is_raisable(A()))
def test_is_raisable_excluded_type_background(self):
"""Test that an exception we want to ignore isn't caught."""
class BadCustomException:
def __init__(self):
raise KeyboardInterrupt
self.assertRaises(KeyboardInterrupt, is_raisable, BadCustomException)
def test_is_raisable_excluded_type_we_want(self):
"""Test that an exception we normally want to ignore can be not
ignored."""
class BadCustomException:
def __init__(self):
raise KeyboardInterrupt
self.assertTrue(is_raisable(BadCustomException, exceptions_to_exclude=()))
def test_is_raisable_not_raisable(self):
"""Test that something not raisable isn't considered rasiable."""
self.assertFalse(is_raisable("test"))
不幸的是,我们需要继续支持Python2.6+(很快就只支持Python2.7,所以如果你有一个在2.6中不起作用的解决方案,那很好,但并不理想)和Python3.x。理想的情况下,我希望在没有显式测试版本的情况下这样做,但是如果没有其他方法,那就没问题了。在
最后,我的问题是:
KeyboardInterrupt
。在TypeError
(一种是因为它有效,另一种是因为它不起作用),这也让人觉得奇怪(但无论如何,我还是得依靠它来获得2.x的支持)。在
如果要检测旧样式的类和实例,只需显式检查它们:
您可能希望将其包装在某种版本检查中,这样它就不会在python3上失败。在
您可以引发对象,捕获异常,然后使用
is
关键字检查引发的异常是否是对象或对象的实例。如果引发了其他任何内容,则为TypeError
,表示该对象不可提升。在此外,要处理任何可提升的对象,我们可以使用^{} 。这还将捕捉异常,例如
KeyboardInterrupt
,但是如果与参数的比较没有结果,我们可以重新调用它们。在在Python中测试大多数东西的方法是
try
,然后查看是否出现异常。在这对
raise
很好。如果某些内容不可引发,则会得到一个TypeError
;否则,您将得到您所引发的内容(或您所引发内容的实例)。这对2.6(甚至2.3)和3.6一样有效。在2.6中作为异常的字符串将是可raise的;不从3.6中的BaseException
继承的类型将不可raise;等等-您可以为所有内容获得正确的结果。不需要检查BaseException
或以不同的方式处理旧样式和新样式的类;只需让raise
完成它的工作。在当然我们需要特殊情况},因此我们需要短路并返回
TypeError
,因为它会降落在错误的地方。但是,由于我们不关心2.4之前的版本,因此不需要任何比isinstance
和issubclass
更复杂的测试;除了返回False
之外,再也没有什么奇怪的对象可以做了。一个棘手的地方(我最初弄错了;感谢user2357112捕捉到它)是您必须首先执行isinstance
测试,因为如果对象是TypeError
实例,issubclass
将引发{True
,而不进行尝试。在另一个问题是处理我们不想意外捕获的任何特殊异常,比如}。但幸运的是,these all go back to before 2.6。而且^{}/^{} 和{a3}(只要你不关心捕捉异常值,我们不关心)都可以使用同样适用于3.x的语法获取元组。因为对于这些情况,我们需要返回
KeyboardInterrupt
和{True
,所以我们需要在尝试提升它们之前测试它们。但它们都是BaseException
子类,所以我们不必担心经典类或类似的东西。在所以:
这并不能通过您编写的测试套件,但我认为这是因为您的一些测试不正确。我假设您希望},然后尝试{}会失败,对吧?所以,在我的头顶上:
is_raisable
对当前Python版本中可raise的对象为true,而不是在任何受支持的版本中可raise的对象,即使它们在当前版本中不可raise。你不会希望is_raisable('spam')
在3.6中返回{not_raisable
测试会引发一个字符串,但是在2.6中这些字符串是可以被提升的。在excluded_type
测试引发了一个类,python2.x可以通过实例化类来处理这个类,但这不是必需的,cpython2.6在这种情况下会触发优化。在old_style
测试在3.6中生成了新样式的类,并且它们不是BaseException
的子类,因此它们不能被提升。在我不知道如果不为2.6、3.x甚至2.7编写单独的测试,甚至可能为两个2.x版本的不同实现编写正确的测试(尽管可能您在Jython上没有任何用户)。在
相关问题 更多 >
编程相关推荐