向所有python异常添加额外信息

向所有python异常添加额外信息,python,oop,exception,exception-handling,decorator,Python,Oop,Exception,Exception Handling,Decorator,在又一次可怕的虫子搜寻之后,我想知道以下几点: 是否可以向所有异常添加一些额外信息,例如对象的名称。 这将大大提高错误的可读性,并使查找错误(或输入错误)的速度大大加快。如果一个人有许多来自同一个类的对象,因此共享许多代码,但具有不同的属性,则情况尤其如此。在这种情况下,如果错误消息还声明了错误中对象的名称,那么它将非常有用 一个简单的例子:我试图模拟不同类型的设施,一个养猪场和一个奶牛场。它们是相同的类,但具有不同的属性。在模拟中,会生成许多工具,如果引发异常,则将对象的名称添加到异常中会非常

在又一次可怕的虫子搜寻之后,我想知道以下几点: 是否可以向所有异常添加一些额外信息,例如对象的名称。 这将大大提高错误的可读性,并使查找错误(或输入错误)的速度大大加快。如果一个人有许多来自同一个类的对象,因此共享许多代码,但具有不同的属性,则情况尤其如此。在这种情况下,如果错误消息还声明了错误中对象的名称,那么它将非常有用

一个简单的例子:我试图模拟不同类型的设施,一个养猪场和一个奶牛场。它们是相同的类,但具有不同的属性。在模拟中,会生成许多工具,如果引发异常,则将对象的名称添加到异常中会非常有用

class facility():
    def __init__(self, name):
        self.name = name
        self.animals = []

farms = []
farms.append(facility('cow_farm'))
farms.append(facility('pig_farm'))
print farms[0].stock
这将导致

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: facility instance has no attribute 'stock'

但这不起作用。是否可以这样做,或者是否有充分的理由不这样做?

如果您想处理错误并添加信息,可以按如下方式执行:

farm = farms[0]
try:
    print farm.stock
except AttributeError:
    raise AttributeError("{} has no attribute 'stock'".format(farm.name))
但是,在
\uuuu init\uuuu
中添加空的
股票可能更明智,以避免此错误

您应该这样做,因为它对您隐藏了有用的信息(特别是在开发和调试时!),通常,每个
try
块应该尽可能短,最好只做一件事。如果单个
try
块可能导致多个错误,则可以添加多个处理程序:

try:
    print farm.stock["hay"]
except AttributeError:
    raise AttributeError("{} has no attribute 'stock'".format(farm.name))
except KeyError:
    raise KeyError("{} has no 'hay' in 'stock'".format(farm.name))
(请注意,在
中添加
self.stock
,并在farm.stock:
中检查
是否为“hay”,可以避免此错误处理。)

如果发生了您没有预料到的错误,通常最好将该错误向上传播到调用堆栈,直到显式处理或看到它为止。否则,您将走向这种愚蠢的反模式:

def some_func(*args, **kwargs):
    try:
        # all of some_func's content goes here
    except:
        raise Exception("Something went wrong in some_func().")
这对您毫无用处,对任何试图使用您的代码的人来说都是非常令人沮丧的

如果您想在
级别处理这样的
属性错误
s,可以执行以下操作:

class Facility(object):

    def __init__(self, ...):
        ...

    def __getattr__(self, key):
        """Called on attempt to access attribute instance.key."""
        if key not in self.__dict__:
            message = "{} instance '{}' has no attribute '{}'."
            message = message.format(type(self).__name__,
                                     self.name, key)
            raise AttributeError(message)
        else:
            return self.__dict__[key]
然后你会得到

>>> farm = Facility("pig farm")
>>> print farm.stock
...
"AttributeError: Facility instance 'pig farm' has no attribute 'stock'."
如果要将此模式用于多个类,可以创建一个超类:

class ProtectedAttrs(object):

    def __init__(self, name):
        self.name = name

    def __getattr__(self, key):
        ...

class Facility(ProtectedAttrs):

    def __init__(self, name):
        super(Facility, self).__init__(name)
        self.animals = []

这种方法适用于某些类型的错误。但是,我不知道有任何常规方法可以处理所有引用相关实例的错误。

大多数异常都包含message属性,它为您提供有关错误的其他信息

In [163]: try:
   .....:     farm = []
   .....:     farm.stock
   .....: except AttributeError as err:
   .....:     print err.message
   .....:     
'list' object has no attribute 'stock'

异常回溯将您指向一个代码行,因此通常不难找出问题所在

啊,被打败了!谢谢,但我正在寻找一个更通用的解决方案,其中也包括意外的异常。例如,如果设施有一个可以引发异常的函数,或者如果出现索引器,等等。我已经编辑了我的答案,以扩展错误处理实践。如果错误不是预期的,您如何知道如何最好地描述它?错误应该向上传播到调用堆栈,但是当我看到它时,我想看看是cow_农场还是pig_农场引发了错误。当程序中有100个不同的工具时,如果错误消息仅表明某个工具实例引发了错误,则很难找到正确的工具。最好不要在每次调用工具时都使用try-except子句。我在回答中添加了更多内容,这将正确处理
AttributeError
。这将告诉您类的名称,而不是实例的名称,这就是OP想要的,如果你得到一个你的代码无法处理的异常——这是一个bug,你无论如何都必须调试它。因此,您将转到代码,您将看到是哪个对象导致了问题。如果异常是预期的并得到了处理,那么您并不真正关心对象实体。我发现在代码中添加信息是绝对多余的。从代码中你可以看到哪个对象导致了异常,但看不到哪个实例。这可能是非常有价值的信息。。。。。您可以从代码块中的变量中获取。如果您只有一个实例,那么在消息中指定它已导致异常是绝对无用的。您将受到大多数对象没有
名称
属性的限制。如果
索引器
在代码中随机出现,您希望看到什么?
class ProtectedAttrs(object):

    def __init__(self, name):
        self.name = name

    def __getattr__(self, key):
        ...

class Facility(ProtectedAttrs):

    def __init__(self, name):
        super(Facility, self).__init__(name)
        self.animals = []
In [163]: try:
   .....:     farm = []
   .....:     farm.stock
   .....: except AttributeError as err:
   .....:     print err.message
   .....:     
'list' object has no attribute 'stock'