我正在编写自己的容器,需要通过属性调用来访问内部的字典。容器的典型用法是这样的:
dict_container = DictContainer()
dict_container['foo'] = bar
...
print dict_container.foo
我知道写这样的东西可能是愚蠢的,但这是我需要提供的功能。我正在考虑以下列方式实现这一点:
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块是否是一个好习惯,另一种方法是使用hasattr()
和has_key()
:
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块,如下所示:
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")
哪个选项最诡异和优雅?
答案 0 :(得分:134)
你的第一个例子非常好。即使是官方的Python文档也推荐使用这种称为EAFP的方式。
就个人而言,我更愿意在没有必要时避免嵌套:
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。 {2}已在Python 2中弃用has_key()
。请改用item in self.dict
。
答案 1 :(得分:14)
虽然在Java中使用Exceptions进行流控制确实是一种不好的做法(主要是因为异常迫使jvm收集资源(more here)),但在Python中你有两个重要的原则:Duck Typing和EAFP。这基本上意味着我们鼓励你以你认为可行的方式尝试使用对象,并在事情不那样时处理。
总之,唯一的问题是你的代码变得太多缩进。如果您愿意,请尝试简化一些嵌套,如lqc建议
答案 2 :(得分:8)
对于您的具体示例,您实际上不需要嵌套它们。如果try
块中的表达式成功,则函数将返回,因此只有在第一次尝试失败时才会运行整个try / except块之后的任何代码。所以你可以这样做:
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"
嵌套它们并不坏,但我觉得保持平整会使结构更清晰:你会依次尝试一系列事情并返回第一个有效的事物。
顺便提一下,您可能想要考虑是否真的想在这里使用__getattribute__
而不是__getattr__
。使用__getattr__
将简化操作,因为您将知道正常的属性查找过程已经失败。
答案 3 :(得分:7)
在我看来,这将是处理它的最恐怖的方式,虽然因为它使你的问题没有实际意义。请注意,这定义了__getattr__()
而不是__getattribute__()
,因为这样做意味着它只需要处理内部字典中保留的“特殊”属性。
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")
答案 4 :(得分:4)
在Python中,要求宽恕比获得更容易。不要让嵌套的异常处理失败。
(此外,has*
几乎总是在封面下使用例外。)
答案 5 :(得分:4)
请注意 - 在这种情况下,首先触摸 public BindableMap()
{
GetPins();
}
public async void GetPins()
{
BusinessRegView B = new BusinessRegView();
foreach (var i in B.BusinessRegList)
{
PinCollection.Add(new Pin() { Position = new Position(i.Latitude, i.Longitude), Type = PinType.Generic, Label = i.BusinessFantasyName });
}
}
但也跳过了。
finally
答案 6 :(得分:2)
根据documentation,最好通过元组处理多个异常,或者像这样:
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
答案 7 :(得分:2)
嵌套try / except的一个很好的简单示例如下:
import numpy as np
def divide(x, y):
try:
out = x/y
except:
try:
out = np.inf * x / abs(x)
except:
out = np.nan
finally:
return out
现在尝试各种组合,您将获得正确的结果:
divide(15, 3)
# 5.0
divide(15, 0)
# inf
divide(-15, 0)
# -inf
divide(0, 0)
# nan
[当然,我们有numpy,所以我们不需要创建此函数]
答案 8 :(得分:1)
我喜欢避免的一件事是在处理旧的异常时引发新的异常。它使错误信息令人困惑。
例如,在我的代码中,我最初写的是
try:
return tuple.__getitem__(self, i)(key)
except IndexError:
raise KeyError(key)
我收到了这条消息。
>>> During handling of above exception, another exception occurred.
我想要的是:
try:
return tuple.__getitem__(self, i)(key)
except IndexError:
pass
raise KeyError(key)
它不会影响异常的处理方式。在任何一个代码块中,都会捕获KeyError。这只是获得风格点的问题。
答案 9 :(得分:0)
我不认为这是pythonic或优雅的问题。 这是一个尽可能多地防止例外的问题。 例外是为了处理您无法控制的代码或事件中可能发生的错误。 在这种情况下,您可以在检查项目是属性还是字典时完全控制,因此请避免嵌套异常并坚持第二次尝试。
答案 10 :(得分:0)
如果try-except-finally嵌套在finally块中,则最终保留“ child”的结果。我还没有找到正式的解释,但是下面的代码片段显示了Python 3.6中的这种行为。
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