Django/Python code: readable vs fast code - python

Lets say I want to get a record that match a value and I have 2 approaches to do it:
First:
try:
obj = Model.objects.get(field = value)
except
pass
Second:
if Model.objects.filter(field = value).count() > 0:
obj = Model.objects.filter(field_value)[0]
Lets put the code comments aside,which way should I use or which one do you prefer to read? The first one seems faster because only 1 DB look up, but the second way seems a bit more readable, but requires 2 DB look ups.

The first is preferred in Python, based on the EAFP design principle ("Easier to Ask Forgiveness than Permission"). Aside from speed, one advantage of this system is that there is no race condition -- in the second example, if other concurrent access to the database changes the results between the execution of the first and the second line of code, then your results will be inconsistent.
Depending on your use of transactions, the race condition possibility may be a non-issue, but in general EAFP is a prominent design pattern in Python and experienced Python coders will not have any trouble reading code in that form.
ETA: Oh, and I forgot: don't use except: (you do need the colon). Use except IndexError: or whatever other specific exception you are looking for. That way if you get a totally unexpected error like a failure to reach the database, it will propagate through and not be hidden. You don't want a situation where you later write code counting on the exception being thrown to mean "there are no results" and instead the system is trying to tell you "the database is down".

How about
matches = Model.objects.filter(field=value)[:1]
obj = matches[0] if matches else None
The query will only be evaluated once (when evaluating matches in the if part of the conditional expression), and this is used both for the emptiness check and retrieving the result. Note the slicing to limit the number of returned objects.

Related

"Ask forgiveness not permission" - explain

I'm not asking for personal "religious" opinions about this philosophy, rather something a bit more technical.
I understand this phrase is one of several litmus tests to see if your code is "pythonic". But to me, pythonic means clean, simple and intuitive, not loaded with exception handlers for bad coding.
So, practical example. I define a class:
class foo(object):
bar = None
def __init__(self):
# a million lines of code
self.bar = "Spike is my favorite vampire."
# a million more lines of code
Now, coming from a procedural background, in another function I wanna do this:
if foo.bar:
# do stuff
I'll get an attribute exception if I was impatient and did not do the initial foo = None. So, "ask forgiveness not permission" suggests I should do this instead?
try:
if foo.bar:
# do stuff
except:
# this runs because my other code was sloppy?
Why would it be better for me to add additional logic in a try block just so I can leave my class definition more ambiguous? Why not define everything initially, therfore explicitly grant permission?
(Don't beat me up about using try/except blocks... I use them everywhere. I just don't think it's right to use them to catch my own errors because I wasn't a thorough programmer.)
Or... do I completely misunderstand the "Ask Forgivess" mantra?
“Ask forgiveness, not permission” opposes two programming styles. “Ask for permission” goes like this:
if can_do_operation():
perform_operation()
else:
handle_error_case()
“Ask forgiveness” goes like this:
try:
perform_operation()
except Unable_to_perform:
handle_error_case()
This is a situation where it is expected that attempting to perform the operation might fail, and you have to handle the situation where the operation is impossible, one way or another. For example, if the operation is accessing a file, the file might not exist.
There are two main reasons why it's better to ask for forgiveness:
In a concurrent world (in a multithreaded program, or if the operation involves objects that are external to the program such as files, other processes, network resources, etc.), the situation might change between the time when you run can_do_operation() and the time when you run perform_operation(). So you'd have to handle the error anyway.
You need to use exactly the right criteria for asking permission. If you get it wrong, you'll either be unable to perform an operation that you could perform, or have an error occur because you can't perform the operation after all. For example, if you test whether a file exists before opening it, it's possible that the file does exist, but you can't open it because you don't have permission. Conversely, maybe the file is created when you open it (for example because it comes over a network connection that is only brought up when you actually open the file, not when you only poke to see whether it's there).
What ask-forgiveness situations have in common is that you're attempting to perform an operation, and you know that the operation may fail.
When you write foo.bar, the non-existence of bar is not normally considered a failure of the object foo. It's usually a programmer error: attempting to use an object in a way that it wasn't designed for. The consequence of a programmer error in Python is an unhandled exception (if you're lucky: of course, some programmer errors can't be detected automatically). So if bar is an optional part of the object, the normal way to deal with this is to have a bar field that's initialized to None, and set to some other value if the optional part is present. To test whether bar is present, write
if foo.bar is not None:
handle_optional_part(foo.bar)
else:
default_handling()
You can abbreviate if foo.bar is not None: to if foo.bar: only if bar will always be true when interpreted as a boolean — if bar could be 0, [], {} or any other object that has a false truth value, you need the is not None. It's also clearer, if you're testing for an optional part (as opposed to testing between True and False).
At this point you may ask: why not omit the initialization of bar when it's not there, and test its presence with hasattr or catch it with an AttributeError handler? Because your code only makes sense in two cases:
the object has no bar field;
the object has a bar field that means what you think it means.
So when writing or deciding to use the object, you need to make sure that it doesn't have a bar field with a different meaning. If you need to use some different object that has no bar field, that's probably not the only thing you'll need to adapt, so you'll probably want to make a derived class or encapsulate the object in another one.
The classical "ask forgiveness not permission" example is accessing values from a dict that may not exist. E.g.:
names = { 'joe': 'Joe Nathan', 'jo': 'Jo Mama', 'joy': 'Joy Full' }
name = 'hikaru'
try:
print names[name]
except KeyError:
print "Sorry, don't know this '{}' person".format(name)
Here the exception that might occur (KeyError) is stated, so that you're not asking forgiveness for every error that might occur, but only the one that would naturally occur.
For comparison, the "ask permission first" approach might look like:
if name in names:
print names[name]
else:
print "Sorry, don't know this '{}' person".format(name)
or
real_name = names.get(name, None)
if real_name:
print real_name
else:
print "Sorry, don't know this '{}' person".format(name)
Such examples of "ask forgiveness" are often too simple. IMO it's not crystal clear that try/except blocks are inherently better than if/else. The real value is much clearer when performing operations that might fail in a variety of ways--such as parsing; using eval(); accessing operating system, middleware, database, or network resources; or performing complex mathematics. When there are multiple potential failure modes, being prepared to get forgiveness is hugely valuable.
Other notes about your code examples:
You do not need to ladle try/except blocks around every variable usage. That would be horrible. And you don't need to set self.bar in your __init__() since it's set in your class definition above. It is usual to define it either in the class (if it's data likely to be shared among all instances of the class) or in the __init__() (if it's instance data, specific to each instance).
A value of None is not undefined, or an error, by the way. It's a specific and legitimate value, meaning none, nil, null, or nothing. Many languages have such values so programmers don't "overload" 0, -1, '' (empty string) or similar useful values.
There's lots of good answers here, I just thought I would add a point I have not seen mentioned so far.
Often asking for forgiveness instead of permission has performance improvements.
When you ask for permission, you have to perform an extra operation
to ask for permission every time.
When asking for forgiveness,
you only have to perform an extra operation sometimes, i.e. when
it fails.
Usually the failure cases are rare, which means if you are only asking for permission, then you hardly ever have to do any extra operations.
Yes, when it fails it throws an exception, as well as performing an extra operation, but exceptions in python are very fast. You can see some timings here: https://jeffknupp.com/blog/2013/02/06/write-cleaner-python-use-exceptions/
You're right -- the purpose of try and except are not to cover your sloppy coding. That just leads to more sloppy coding.
Exception handling should be used to handle exceptional circumstances (sloppy coding is not an exceptional circumstance). However, often, it is easy to predict which exceptional circumstances might happen. (e.g. a your program accepts user input and uses that to access a dictionary, but the user's input wasn't a key in the dictionary ...)
My personal non-religious opinion is that said mantra applies mainly to documented and well understood exit conditions and edge cases (e.g. I/O errors), and should never be used as a get-out-of-jail card for sloppy programming.
That said, try/except is often used when "better" alternatives exist. For example:
# Do this
value = my_dict.get("key", None)
# instead of this
try:
value = my_dict["key"]
except KeyError:
value = None
As for your example, do use if hasattr(foo, "bar") if your have no control over foo and need to check conformance with your expectations, otherwise simply use foo.bar and let the resulting error be your guide to identifying and fixing sloppy code.
While there's already a number of high quality answers, most primarily discuss this from a stylistic stand point, as appose to a functional one.
There are certain cases where we need to ask forgiveness, not permission to insure correct code (outside of a multithreaded programs).
A canonical example being,
if file_exists:
open_it()
In this example, the file could have been deleted between the check and trying to actually open the file. This is avoided by using try:
try:
open_it()
except FileNotFoundException:
pass # file doesn't exist
This crops up in a variety of places, often working with filesystems or external APIs.
In the Python context "Ask forgiveness not permission" implies a style of programming where you don't check for things to be as you expect beforhand, but rather you handle the errors that result if they are not. The classical example is not to check that a dictionary contains a given key as in:
d = {}
k = "k"
if k in d.keys():
print d[k]
else:
print "key \"" + k + "\" missing"
But rather to handle the resulting error if the key is missing:
d = {}
k = "k"
try:
print d[k]
except KeyError:
print "key \"" + k + "\" missing"
However the point is not to replace every if in your code with a try/except; that would make your code decidedly messier. Instead you should only catch errors where you really can do something about them. Ideally this would reduce the amount of overall error handling in your code, making its actual purpose more evident.
Ask forgiveness not permission is meant to simplify code. It's meant for code to be written like this when there's a reasonable expectation that .bar might trigger an AttributeError.
try:
print foo.bar
except AttributeError as e
print "No Foo!"
Your code appears to both ask permission AND forgiveness :)
The thing is, if you reasonably expect something to fail, use a try/catch. If you don't expect something to fail, and it fails anyway, well the exception that gets thrown becomes the equivalent of a failed assertion in other languages. You see where the unexpected exception is occurring and adjust your code/assumptions accordingly.

Is there any value to a Switch / Case implementation in Python?

Recently, I saw some discussions online about how there is no good "switch / case" equivalent in Python. I realize that there are several ways to do something similar - some with lambda, some with dictionaries. There have been other StackOverflow discussions about the alternatives. There were even two PEPs (PEP 0275 and PEP 3103) discussing (and rejecting) the integration of switch / case into the language.
I came up with what I think is an elegant way to do switch / case.
It ends up looking like this:
from switch_case import switch, case # note the import style
x = 42
switch(x) # note the switch statement
if case(1): # note the case statement
print(1)
if case(2):
print(2)
if case(): # note the case with no args
print("Some number besides 1 or 2")
So, my questions are: Is this a worthwhile creation? Do you have any suggestions for making it better?
I put the include file on github, along with extensive examples. (I think the entire include file is about 50 executable lines, but I have 1500 lines of examples and documentation.) Did I over-engineer this thing, and waste a bunch of time, or will someone find this worthwhile?
Edit:
Trying to explain why this is different from other approaches:
1) Multiple paths are possible (executing two or more cases),
which is harder in the dictionary method.
2) can do checking for comparisons other than "equals"
(such as case(less_than(1000)).
3) More readable than the dictionary method, and possibly if/elif method
4) can track how many True cases there were.
5) can limit how many True cases are permitted. (i.e. execute the
first 2 True cases of...)
6) allows for a default case.
Here's a more elaborate example:
from switch_case import switch, case, between
x=12
switch(x, limit=1) # only execute the FIRST True case
if case(between(10,100)): # note the "between" case Function
print ("%d has two digits."%x)
if case(*range(0,100,2)): # note that this is an if, not an elif!
print ("%d is even."%x) # doesn't get executed for 2 digit numbers,
# because limit is 1; previous case was True.
if case():
print ("Nothing interesting to say about %d"%x)
# Running this program produces this output:
12 has two digits.
Here's an example attempting to show how switch_case can be more clear and concise than conventional if/else:
# conventional if/elif/else:
if (status_code == 2 or status_code == 4 or (11 <= status_code < 20)
or status_code==32):
[block of code]
elif status_code == 25 or status_code == 45:
[block of code]
if status_code <= 100:
[block can get executed in addition to above blocks]
# switch_case alternative (assumes import already)
switch(status_code)
if case (2, 4, between(11,20), 32): # significantly shorter!
[block of code]
elif case(25, 45):
[block of code]
if case(le(100)):
[block can get executed in addition to above blocks]
The big savings is in long if statements where the same switch is repeated over and over. Not sure how frequent of a use-case that is, but there seems to be certain cases where this makes sense.
The example file on github has even more examples.
So, my questions are: Is this a worthwhile creation?
No.
Do you have any suggestions for making it better?
Yes. Don't bother. What has it saved? Seriously? You have actually made the code more obscure by removing the variable x from each elif condition.. Also, by replacing the obvious elif with if you have created intentional confusion for all Python programmers who will now think that the cases are independent.
This creates confusion.
The big savings is in long if statements where the same switch is repeated over and over. Not sure how frequent of a use-case that is, but there seems to be certain cases where this makes sense.
No. It's very rare, very contrived and very hard to read. Seeing the actual variable(s) involved is essential. Eliding the variable name makes things intentionally confusing. Now I have to go find the owning switch() function to interpret the case.
When there are two or more variables, this completely collapses.
There have been a plethora of discussions that address this issue on Stackoverflow. You can use the search function at the top to look for some other discussions.
However, I fail to see how your solution is better than a basic dictionary:
def switch(x):
return {
1 : 1,
2 : 2,
}[x]
Although, adding a default clause is non-trivial with this method. However, your example seems to replicate a complex if/else statement anyway ? Not sure if I would include an external library for this.
IMHO, the main reason for the switch statement to exist is so it can be translated/compiled into a (very fast) jump table. How would your proposed implementation accomplish that goal? Python's dictionaries do it today, as other posters have shown.
Secondarily, I guess a switch statement might read more clearly than the alternatives in some languages, but in python's case I think if/elif/else wins on clarity.
from pyswitch import Switch # pyswitch can be found on PyPI
myswitch = Switch()
#myswitch.case(42)
def case42(value):
print "I got 42!"
#myswitch.case(range(10))
def caseRange10(value):
print "I got a number from 0-9, and it was %d!" % value
#myswitch.caseIn('lo')
def caseLo(value):
print "I got a string with 'lo' in it; it was '%s'" % value
#myswitch.caseRegEx(r'\b([Pp]y\w)\b')
def caseReExPy(matchOb):
print r"I got a string that matched the regex '\b[Pp]y\w\b', and the match was '%s'" % matchOb.group(1)
#myswitch.default
def caseDefault(value):
print "Hey, default handler here, with a value of %r." % value
myswitch(5) # prints: I got a number from 0-9, and it was 5!
myswitch('foobar') # prints: Hey, default handler here, with a value of foobar.
myswitch('The word is Python') # prints: I got a string that matched the regex '\b[Pp]y\w\b', and the match was 'Python'
You get the idea. Why? Yep, dispatch tables are the way to go in Python. I just got tired of writing them over and over, so I wrote a class and some decorators to handle it for me.
I have always just used dictionaries, if/elses, or lambdas for my switch like statements. Reading through your code tho =)
docs:
why-isn-t-there-a-switch-or-case-statement-in-python
Update 2021: match-case introduced in Python 3.10
This hotly debated topic can now be closed.
In fact Python 3.10 released in October 2021 introduces structural pattern matching which brings a match-case construct to the language.
See this related answer for details.

In simple cases, is it better to except & recover or to avoid the exception?

Consider:
categories = {'foo':[4], 'mer':[2, 9, 0]}
key = 'bar'
value = 5
We could safely append to a list stored in a dictionary in either of the following ways:
Being cautious, we always check whether the list exists before appending to it.
if not somedict.has_key(key):
somedict[key] = []
somedict[key].append(value)
Being direct, we simply clean up if there is an exception.
try:
somedict[key].append(value)
except KeyError:
somedict[key] = [value]
In both cases, the result could be:
{'foo':[4], 'mer':[2, 9, 0], 'bar':[5]}
To restate my question: In simple instances like this, is it better (in terms of style, efficiency, & philosophy) to be cautious or direct?
What you'll find is that your option 1 "being cautious" is often remarkably slow. Also, it's subject to obscure errors because the test you tried to write to "avoid" the exception is incorrect.
What you'll find is that your option 2 "being direct" is often much faster. It's also more likely to be correct, as well as faster and easier for people to read.
Why? Internally, Python often implements things like "contains" or "has_key" as an exception test.
def has_key( self, some_key ):
try:
self[some_key]
except KeyError:
return False
return True
Since this is typically how a has_key type of method is implemented, there's no reason for you code do waste time doing this in addition to what Python will already do.
More fundamentally, there's a correctness issue. Many attempts to prevent or avoid an exception are incomplete are incorrect.
For example, trying to establish if a string is potentially a float-point number is fraught with numerous exceptions and special cases. About the only way to do it correctly is this.
try:
x= float( some_string )
except ValueError:
# not a floating-point value
Just do the algorithm without worrying about "preventing" or "avoiding" exceptions.
In the general case, EFAP ("easier to ask for forgiveness than permission") is preferred in Python. Of course the rule of thumb "exceptions should be for exceptional cases" still holds (if you expect an exception to occur frequently, you propably should "look before you leap") - i.e. it depends. Efficiency-wise, it shouldn't make too much of a difference in most cases - when it does, consider that try blocks without exceptions are cheap and conditions are always checked.
Note that neither is necessary (at least you don't have to do it yourself/epplicitly) some cases, including this example - here, you should just use collections.defaultdict
You don't need a strong, compelling reason to use exceptions--they're not very expensive in Python. Here are some possible reasons to prefer one or the other, for your particular example:
The exception version requires a simpler API. Any container that supports item lookup and assignment (__getitem__ and __setitem__) will work. The non-exception version additionally requires that has_key be implemented.
The exception version may be slightly faster if the key usually exists, since it only requires a single dict lookup. The has_key version requires at least two--one for has_key and one for the actual lookup.
The non-exception version has a more consistent code path: it always puts the value in the array in the same place. By comparison, the exception version has a separate code path for each case.
Unless performance is particularly important (in which case you'd be benchmarking and profiling), none of these are very strong reasons; just use whichever seems more natural.
try is fast enough, except (if it happens) may not be. If the average length of those lists is going to be 1.1, use the check-first method. If it's going to be in the thousands, use try/except. If you are really worried, benchmark the alternatives.
Ensure that you are benchmarking the best alternatives. d.has_key(k) is a slow old has_been; you don't need the attribute lookup and the function call. Use k in d instead. Also use else to save a wasted append on the first trip:
Instead of:
if not somedict.has_key(key):
somedict[key] = []
somedict[key].append(value)
do this:
if key in somedict:
somedict[key].append(value)
else:
somedict[key] = [value]
You can use setdefault for this specific case:
somedict.setdefault(key, []).append(value)
See here: http://docs.python.org/library/stdtypes.html#mapping-types-dict
It depends, for exemple if the key is a paramenter of a function that will be used by an other programer, I would use the second approach, because I can't control the input, and the exception information it's actually usefull for a programer. But if its just a process inside a function and the key it's just some input from a database for exemple, the first approach it's better, then if something goes wrong, maybe show the exception information isn't helpfull at all. Use the exception approach if you want to do someting with the exception information.
EFAP is a good habit to get into for Python.
One reason is that it avoids the race condition if someone wants to use your code in a multithreaded app

Using 'try' vs. 'if' in Python

Is there a rationale to decide which one of try or if constructs to use, when testing variable to have a value?
For example, there is a function that returns either a list or doesn't return a value. I want to check result before processing it. Which of the following would be more preferable and why?
result = function();
if (result):
for r in result:
#process items
or
result = function();
try:
for r in result:
# Process items
except TypeError:
pass;
Related discussion:
Checking for member existence in Python
You often hear that Python encourages EAFP style ("it's easier to ask for forgiveness than permission") over LBYL style ("look before you leap"). To me, it's a matter of efficiency and readability.
In your example (say that instead of returning a list or an empty string, the function were to return a list or None), if you expect that 99 % of the time result will actually contain something iterable, I'd use the try/except approach. It will be faster if exceptions really are exceptional. If result is None more than 50 % of the time, then using if is probably better.
To support this with a few measurements:
>>> import timeit
>>> timeit.timeit(setup="a=1;b=1", stmt="a/b") # no error checking
0.06379691968322732
>>> timeit.timeit(setup="a=1;b=1", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.0829463709378615
>>> timeit.timeit(setup="a=1;b=0", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
0.5070195056614466
>>> timeit.timeit(setup="a=1;b=1", stmt="if b!=0:\n a/b")
0.11940114974277094
>>> timeit.timeit(setup="a=1;b=0", stmt="if b!=0:\n a/b")
0.051202772912802175
So, whereas an if statement always costs you, it's nearly free to set up a try/except block. But when an Exception actually occurs, the cost is much higher.
Moral:
It's perfectly OK (and "pythonic") to use try/except for flow control,
but it makes sense most when Exceptions are actually exceptional.
From the Python docs:
EAFP
Easier to ask for forgiveness than
permission. This common Python coding
style assumes the existence of valid
keys or attributes and catches
exceptions if the assumption proves
false. This clean and fast style is
characterized by the presence of many
try and except statements. The
technique contrasts with the LBYL
style common to many other languages
such as C.
Your function should not return mixed types (i.e. list or empty string). It should return a list of values or just an empty list. Then you wouldn't need to test for anything, i.e. your code collapses to:
for r in function():
# process items
Please ignore my solution if the code I provide is not obvious at first glance and you have to read the explanation after the code sample.
Can I assume that the "no value returned" means the return value is None? If yes, or if the "no value" is False boolean-wise, you can do the following, since your code essentially treats "no value" as "do not iterate":
for r in function() or ():
# process items
If function() returns something that's not True, you iterate over the empty tuple, i.e. you don't run any iterations. This is essentially LBYL.
Generally, the impression I've gotten is that exceptions should be reserved for exceptional circumstances. If the result is expected never to be empty (but might be, if, for instance, a disk crashed, etc), the second approach makes sense. If, on the other hand, an empty result is perfectly reasonable under normal conditions, testing for it with an if statement makes more sense.
I had in mind the (more common) scenario:
# keep access counts for different files
file_counts={}
...
# got a filename somehow
if filename not in file_counts:
file_counts[filename]=0
file_counts[filename]+=1
instead of the equivalent:
...
try:
file_counts[filename]+=1
except KeyError:
file_counts[filename]=1
Which of the following would be more preferable and why?
Look Before You Leap is preferable in this case. With the exception approach, a TypeError could occur anywhere in your loop body and it'd get caught and thrown away, which is not what you want and will make debugging tricky.
(I agree with Brandon Corfman though: returning None for ‘no items’ instead of an empty list is broken. It's an unpleasant habit of Java coders that should not be seen in Python. Or Java.)
Your second example is broken - the code will never throw a TypeError exception since you can iterate through both strings and lists. Iterating through an empty string or list is also valid - it will execute the body of the loop zero times.
bobince wisely points out that wrapping the second case can also catch TypeErrors in the loop, which is not what you want. If you do really want to use a try though, you can test if it's iterable before the loop
result = function();
try:
it = iter(result)
except TypeError:
pass
else:
for r in it:
#process items
As you can see, it's rather ugly. I don't suggest it, but it should be mentioned for completeness.
As far as the performance is concerned, using try block for code that normally
doesn’t raise exceptions is faster than using if statement everytime. So, the decision depends on the probability of excetional cases.
As a general rule of thumb, you should never use try/catch or any exception handling stuff to control flow. Even though behind the scenes iteration is controlled via the raising of StopIteration exceptions, you still should prefer your first code snippet to the second.

Is it better to use an exception or a return code in Python?

You may know this recommendation from Microsoft about the use of exceptions in .NET:
Performance Considerations
...
Throw exceptions only for
extraordinary conditions, ...
In addition, do not throw an exception
when a return code is sufficient...
(See the whole text at http://msdn.microsoft.com/en-us/library/system.exception.aspx.)
As a point of comparison, would you recommend the same for Python code?
The pythonic thing to do is to raise and handle exceptions. The excellent book "Python in a nutshell" discusses this in 'Error-Checking Strategies' in Chapter 6.
The book discusses EAFP ("it's easier to ask forgiveness than permission") vs. LBYL ("look before you leap").
So to answer your question:
No, I would not recommend the same for python code. I suggest you read chapter 6 of Python in a nutshell.
The best way to understand exceptions is "if your method can't do what its name says it does, throw." My personal opinion is that this advice should be applied equally to both .NET and Python.
The key difference is where you have methods that frequently can't do what their name says they should do, for instance, parsing strings as integers or retrieving a record from a database. The C# style is to avoid an exception being thrown in the first place:
int i;
if (Int32.TryParse(myString, out i)) {
doWhatever(i);
}
else {
doWhatever(0);
}
whereas Python is much more at ease with this kind of thing:
try:
i = int(myString)
except ValueError:
i = 0
doWhatever(i);
Usually, Python is geared towards expressiveness.
I would apply the same principle here: usually, you expect a function to return a result (in line with its name!) and not an error code.
For this reason, it is usually better raising an exception than returning an error code.
However, what is stated in the MSDN article applies to Python as well, and it's not really connected to returning an error code instead of an exception.
In many cases, you can see exception handling used for normal flow control, and for handling expected situations. In certain environments, this has a huge impact on performance; in all environments it has a big impact on program expressiveness and maintainability.
Exceptions are for exceptional situations, that are outside of normal program flow; if you expect something will happen, then you should handle directly, and then raise anything that you cannot expect / handle.
Of course, this is not a recipe, but only an heuristic; the final decision is always up to the developer and onto the context and cannot be stated in a fixed set of guidelines - and this is much truer for exception handling.
In Python exceptions are not very expensive like they are in some other languages, so I wouldn't recommend trying to avoid exceptions. But if you do throw an exception you would usually want catch it somewhere in your code, the exception being if a fatal error occurs.
I think whether to return an error code or throw an exception is something very valid to think about, and a cross-linguistic comparison may be helpful and informative. I guess the very generalized answer to this concern is simply the consideration: that the set of legal return values for any function should be made as small as possible, and as large as necessary.
Generally, this will mean that if a given method returns an integer number in a single test case, users can rightfully expect the method to always return an integer number or throw an exception. But, of course, the conceptually simplest way is not always the best way to handle things.
The return-value-of-least-surprise is usually None; and if you look into it, you’ll see that it’s the very semantics of None that license its usage across the board: it is a singleton, immutable value that, in a lot of cases, evaluates to False or prohibits further computation—no concatenation, no arithmetics. So if you chose to write a frob(x) method that returns a number for a string input, and None for non-numeric strings and any other input, and you use that inside an expression like a=42+frob('foo'), you still get an exception very close to the point where bogus things happened. Of course, if you stuff frob('foo') into a database column that has not been defined with NOT NULL, you might run into problems perhaps months later. This may or may not be justifiable.
So in most cases where you e.g. want to derive a number from a string, using somwething like a bare float(x) or int(x) is the way to go, as these built-ins will raise an exception when not given a digestable input. If that doesn’t suit your use case, consider returning None from a custom method; basically, this return value tells consumers that ‘Sorry, I was unable to understand your input.’. But you only want to do this if you positively know that going on in your program does make sense from that point onwards.
You see, I just found out how to turn each notice, warning, and error message into a potentially show-stopping exception in, uhm, PHP. It just drives me crazy that a typo in a variable name generates in the standard PHP configuration, nothing but a notice to the user. This is so bad. The program just goes on doing things with a program code that does not make sense at all! I can’t believe people find this a feature.
Likewise, one should view it like this: if, at any given point in time, it can be asserted with reasonable costs that the execution of a piece of code does no more make sense — since values are missing, are out of bounds, or are of an unexpected type, or when resources like a database connection have gone down — it is imperative, to minimize debugging headaches, to break execution and hand control up to any level in the code which feels entitled to handle the mishap.
Experience shows that refraining from early action and allowing bogus values to creep into your data is good for nothing but making your code harder to debug. So are many examples of over-zealous type-casting: allowing integers to be added to floats is reasonable. To allow a string with nothing but digits to be added to a number is a bogus practice that is likely to create strange, unlocalized errors that may pop up on any given line that happens to be processing that data.
I did a simple experiment to compare the performance of raising exceptions with the following code:
from functools import wraps
from time import time
import logging
def timed(foo):
#wraps(foo)
def bar(*a, **kw):
s = time()
foo(*a, **kw)
e = time()
print '%f sec' % (e - s)
return bar
class SomeException(Exception):
pass
def somefunc(_raise=False):
if _raise:
raise SomeException()
else:
return
#timed
def test1(_reps):
for i in xrange(_reps):
try:
somefunc(True)
except SomeException:
pass
#timed
def test2(_reps):
for i in xrange(_reps):
somefunc(False)
def main():
test1(1000000)
test2(1000000)
pass
if __name__ == '__main__':
main()
With the following results:
Raising exceptions: 3.142000 sec
Using return: 0.383000 sec
Exceptions are about 8 times slower than using return.

Categories