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. 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.
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. If multiple errors could stem from a single try
block, you can add multiple handlers:
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.)
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 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
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.