Alex Hall wrote:
Hi all,
I am wondering what the best way to do the following would be: throw
an exception, or always return an object but set an error flag if
something goes wrong?
Raise an exception. Error flags are an anti-pattern -- a software idiom
that you should not follow.
The problem with flags is that callers will forget to check them, which
leads to problems being revealed far away from where the problem was
caused. That makes it *really* hard to debug.
result = function(x) # fails and sets an error flag in result
do_something_else()
data = [1, 2, 'a', result, 'xyz'] # store
another_function(data)
# ...
# ...
# lots more code here
# ...
# ...
x = data[3]
do_something_with(x)
which then blows up, because x is invalid but you haven't checked the
error flag. The problem actually was with the *original* x, all the way
back at the start, but that's been thrown away now, never to be seen
again, which makes it hard to debug why it failed.
A million, billion, trillion times worse is if you have a single global
error flag! That's *disastrous*, because you MUST check the flag
*immediately*, otherwise it can be cleared.
x = function(100000000) # fails
y = another_function(1000)
if global_error_flag:
# seems to be safe to use x
process(x) # but it isn't, and this blows up
the problem being that another_function makes a second call to
function(), only this one succeeds and resets the global flag. If you do
this, the ghost of a thousand programmers will drag your spirit off to
the nether regions of Hell, where you will have to debug the Windows
kernel using only the `ed` editor on a keyboard missing the letters "x",
"s" and "1" for all of eternity.
For those who don't know the `ed` editor, it is "the standard Unix editor":
http://www.gnu.org/fun/jokes/ed.msg.html
Which is the "standard" way when dealing with objects? Throw
exceptions or always return an object, even if said object has an
error and may therefore not have data beyond an error code and
message? If I go the exception route, can I somehow put a message into
the exception, maybe adding it as an attribute of my custom exception
class? I assume so...
except e:
print e.message
Most standard is to raise an exception. The syntax is:
raise ValueError("any message you like")
or use whatever error type suits your problem. You can even define your
own exception types:
class MyError(ValueError):
pass
Less common, but still reasonable, is to raise an error sentinel in
place of the normal result. For example, re.match() and re.search()
return None when there is nothing found, instead of a MatchObject.
Another example, str.find() returns -1.
The disadvantage of this is obvious:
>>> string = "Nobody expects the Portuguese Inquisition!"
>>> offset = string.find("Spanish")
>>> print(string[offset:]) # expecting "Spanish Inquisition!"
!
For special purposes, like mathematics, you can define error values that
propagate through calculations. Python has half-hearted support for
such "Not A Number" codes:
>>> nan = float('nan')
>>> nan + 1 # doesn't fail, but propagates
nan
>>> nan**2
nan
>>> nan - 1000
nan
as well as infinity. But you should consider this a very specialized
solution (as well as a lot of work!!!).
Another non-standard solution is to return a pair of values, a flag plus
the value you actually want:
flag, value = function(10000)
if flag:
do_something_with(value)
else:
process_error()
or something like that. I know I have research to do about the
specifics of all this, but before I go off and look it all up I am
wondering if it is the best way to go as far as standards and best
coding practices. This is still that api wrapper, so I am not the only
one who may end up using this file and I want to make it into
something that is useable and does what people expect. TIA.
Then there is absolutely not even a shadow of a doubt: use exceptions.
--
Steven
_______________________________________________
Tutor maillist - [email protected]
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor