Are nested try/except blocks in python a good programming practice?
我正在编写自己的容器,它需要通过属性调用来访问内部的字典。容器的典型用途如下:
1 2 3 4 | dict_container = DictContainer() dict_container['foo'] = bar ... print dict_container.foo |
我知道写这样的东西可能很愚蠢,但这就是我需要提供的功能。我正在考虑以以下方式实施:
1 2 3 4 5 6 7 8 | def __getattribute__(self, item): try: return object.__getattribute__(item) except AttributeError: try: return self.dict[item] except KeyError: print"The object doesn't have such attribute" |
我不确定嵌套的try/except块是否是一个好的实践,因此另一种方法是使用
1 2 3 4 5 6 7 8 | def __getattribute__(self, item): if hasattr(self, item): return object.__getattribute__(item) else: if self.dict.has_key(item): return self.dict[item] else: raise AttributeError("some customised error") |
或者使用其中一个,然后尝试如下catch块:
1 2 3 4 5 6 7 8 | def __getattribute__(self, item): if hasattr(self, item): return object.__getattribute__(item) else: try: return self.dict[item] except KeyError: raise AttributeError("some customised error") |
哪种选择是最优雅的Python?
你的第一个例子很好。甚至官方的python文档也推荐这种风格,称为eafp。
就我个人而言,我更喜欢在不必要的时候避免筑巢:
1 2 3 4 5 6 7 8 9 | def __getattribute__(self, item): try: return object.__getattribute__(item) except AttributeError: pass # fallback to dict try: return self.dict[item] except KeyError: raise AttributeError("The object doesn't have such attribute") from None |
ps.
在Java中,使用流量控制异常(这主要是因为异常迫使JVM收集资源(这里更详细)),这在Python中有2个重要原则:Duck Typing和EAFP。这基本上意味着,我们鼓励您按照您认为的方式使用一个对象,并在事情不这样时进行处理。
总之,唯一的问题是代码缩进太多。如果你喜欢,试着简化一些像LQC建议的嵌套。
对于您的特定示例,实际上不需要嵌套它们。如果
1 2 3 4 5 6 7 8 9 10 | def __getattribute__(self, item): try: return object.__getattribute__(item) except AttributeError: pass # execution only reaches here when try block raised AttributeError try: return self.dict[item] except KeyError: print"The object doesn't have such attribute" |
嵌套它们并不坏,但我觉得让它平放会使结构更清晰:您按顺序尝试一系列事情,并返回第一个有效的。
顺便说一下,您可能想考虑一下您是否真的想在这里使用
在我看来,这将是处理这件事的最卑鄙的方式,尽管也因为这会让你的问题变得毫无意义。请注意,这个定义是OCx1(7),而不是
1 2 3 4 5 6 | def __getattr__(self, name): """only called when an attribute lookup in the usual places has failed""" try: return self.my_dict[name] except KeyError: raise AttributeError("some customized error message") |
小心点——在这种情况下,首先触摸
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def a(z): try: 100/z except ZeroDivisionError: try: print('x') finally: return 42 finally: return 1 In [1]: a(0) x Out[1]: 1 |
在Python中,请求宽恕比请求允许更容易。不要为嵌套的异常处理操心。
(除此之外,
根据文档,最好通过元组或类似的方式处理多个异常:
1 2 3 4 5 6 7 8 9 10 11 12 13 | import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except IOError as e: print"I/O error({0}): {1}".format(e.errno, e.strerror) except ValueError: print"Could not convert data to an integer." except: print"Unexpected error:", sys.exc_info()[0] raise |
我喜欢避免的一件事是在处理旧的异常时引发新的异常。它会使错误消息变得难以阅读。
例如,在我的代码中,我最初编写
1 2 3 4 | try: return tuple.__getitem__(self, i)(key) except IndexError: raise KeyError(key) |
我收到了这个信息。
1 | >>> During handling of above exception, another exception occurred. |
我想要的是:
1 2 3 4 5 | try: return tuple.__getitem__(self, i)(key) except IndexError: pass raise KeyError(key) |
它不会影响异常的处理方式。在这两个代码块中,都会捕获keyError。这只是一个获得风格点的问题。
如果try except finally嵌套在finally块中,则最终保留"child"的结果。我还没有找到正式的解释,但是下面的代码片段在Python3.6中显示了这种行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | def f2(): try: a = 4 raise SyntaxError except SyntaxError as se: print('log SE') raise se from None finally: try: raise ValueError except ValueError as ve: a = 5 print('log VE') raise ve from None finally: return 6 return a In [1]: f2() log SE log VE Out[2]: 6 |
我不认为这是一个关于Python或优雅的问题。这是一个尽可能防止例外的问题。异常是指处理代码或您无法控制的事件中可能发生的错误。在这种情况下,当检查项是属性还是字典时,您拥有完全的控制权,因此避免嵌套的异常,并坚持第二次尝试。