简体   繁体   English

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

[英]Add extra information to all python exceptions

After another terrible bug hunt, I am wondering the following: Is it possible to add some extra information to all exceptions, for example the name of an object. 在又一次可怕的bug搜索之后,我想知道以下内容:是否可以为所有异常添加一些额外信息,例如对象的名称。 This would increase the readability of the errors a lot and make looking for bugs (or input errors) a lot quicker. 这会大大增加错误的可读性,并且可以更快地查找错误(或输入错误)。 This is especially the case if one has many objects which are from the same class and therefore share much code, but have different attributes. 如果有多个对象来自同一个类并因此共享很多代码但具有不同的属性,则尤其如此。 In this case it can be very useful if the error message also states the name of the object in the error. 在这种情况下,如果错误消息还指出错误中对象的名称,则它非常有用。

A simplfied example: I am trying to simulate different types of facilities, a pig farm and a cow farm. 一个简单的例子:我正在尝试模拟不同类型的设施,养猪场和奶牛场。 These are the same class, but do have different attributes. 这些是同一个类,但确实有不同的属性。 In the simulation many facilities are made and if an exception is raised, it would be very helpful if the name of the object is added to the exception. 在模拟中,会创建许多工具,如果引发异常,如果将对象的名称添加到异常中将非常有用。

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

This would yield 这会产生

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

But I would like to add the name of the facility: 但我想添加设施的名称:

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

I tried something like 我试过类似的东西

def print_name(exception):
    try:
        print self.name
    except AttributeError:
        pass
    raise exception

@print_name
Exception

But that doesn't work. 但这不起作用。 Is it possible to do such a thing, or are there good reasons not to do this? 有可能做这样的事情,还是有充分的理由不这样做?

If you want to handle errors and add information, you could do it as follows: 如果要处理错误并添加信息,可以按如下方式执行:

farm = farms[0]
try:
    print farm.stock
except AttributeError:
    raise AttributeError("{} has no attribute 'stock'".format(farm.name))

However, it might be wiser to add the empty stock in __init__ to avoid this error. 但是,在__init__添加空stock以避免此错误可能更明智。

You should never use a bare except , as it hides useful information from you ( particularly when developing and debugging!) Generally, each try block should be as short as possible, preferably doing only one thing. 你应该永远不会使用裸except ,因为它隐藏你有用的信息( 尤其是发展中国家时,调试!)一般情况下,每一个try块应该越短越好,最好做的只有一件事。 If multiple errors could stem from a single try block, you can add multiple handlers: 如果多个错误可能来自单个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))

(Although note that adding self.stock in __init__ and checking if "hay" in farm.stock: would save you from this error handling.) (虽然请注意在__init__中添加self.stock并检查self.stock中的if "hay" in farm.stock:会使您免于此错误处理。)

If an error happens that you weren't expecting, it is generally best for that error to propagate up the call stack until it is explicitly handled or you get to see it. 如果发生了您不期望的错误,通常最好将该错误传播到调用堆栈,直到它被明确处理或您看到它。 Otherwise, you are heading for this foolish anti-pattern: 否则,你正在走向这个愚蠢的反模式:

def some_func(*args, **kwargs):
    try:
        # all of some_func's content goes here
    except:
        raise Exception("Something went wrong in some_func().")

Which is no use to you and extremely frustrating for anyone trying to use your code. 这对你没用,对于任何试图使用你的代码的人来说都是非常令人沮丧的。

If you want to handle AttributeError s like this at the class level, you could do: 如果你想在class级别处理类似这样的AttributeError ,你可以这样做:

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]

Then you will get 然后你会得到

>>> farm = Facility("pig farm")
>>> print farm.stock
...
"AttributeError: Facility instance 'pig farm' has no attribute 'stock'."

If you want to use this pattern with multiple classes, you can make a superclass: 如果要将此模式与多个类一起使用,可以创建一个超类:

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 = []

This sort of thing will work for some types of error. 这种事情适用于某些类型的错误。 However, I am not aware of any general way to handle all errors with references to the instance involved. 但是,我不知道通过引用所涉及的实例来处理所有错误的任何一般方法。

Most of exceptions contain message attribute, that give you additional information about error 大多数异常都包含message属性,可以为您提供有关错误的其他信息

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

Exception traceback points you to a code line, so usually it is not hard to figure out the problem 异常回溯将您指向代码行,因此通常不难发现问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM