简体   繁体   中英

Is creating a variable in try/except block considered a bad practice?

Let's consider this example - code that returns index of the first None value in the list li (if present) or the last element's index if None is not present in li .

I could possibly check for None being in list but using ifs isn't the preferred way - Dive Into Python encourages to use exceptions as they are said to be faster and make code cleaner (see this question). So I came with three approaches:

1.

try:
    my_index = li.index(None)
except ValueError:
    my_index = len(li) - 1

print my_index

my_index is declared inside try/except block. Less lines, no redundant declaration. Possible problem - different exception type will result in not creating my_index and crashing the script on print .

2.

my_index = None  # or anything else
try:
    my_index = li.index(None)
except ValueError:
    my_index = len(li) - 1

print my_index

my_index is declared before try/except and has a value assigned regardless of what exception happens. Cons - more lines, looks redundant.

3. edit: Won't work - finally is executed regardless of try/except results

 try: my_index = li.index(None) except ValueError: my_index = len(li) - 1 finally: my_index = None # or anything else print my_index 

Pros - always works (or at least I think so). Cons - I have never ever seen anyone using finally for my entire Python experience which must happen for a reason.

Which of these methods is considered the preferred and pythonic way? Maybe there is some better solution?

 try: my_index = li.index(None) except ValueError: my_index = len(li) - 1 print my_index 

Possible problem - different exception type will result in not creating my_index and crashing the script on print .

Err, no. If any other exception is raised, this entire block of code will be aborted as the exception bubbles up to the caller. So if any other exception type besides ValueError is raised, print will never be executed. Which is a pretty sane behaviour: you handle the errors you expect and can handle, and let other errors cause the entire block to abort, possibly the entire script. This is the sanest and most concise solution for what you want.

The third option won't work. Check out the documentation on finally (emphasis mine):

A finally clause is always executed before leaving the try statement, whether an exception has occurred or not.

So in the third option, no matter what happens, my_index is always none.

That leaves us with the first and second option. It is worth noting that the second one is what you'll see in most statically typed languages. Python however is a bit more lenient and allows for either option.

Your argument regarding an exception that isn't ValueError is, I think, invalid. Another exception will halt execution and go back to the caller, so the print will never be reached. So the first solution should work just fine.

++ As @deceze noticed already, except ValueError: will catch only errors of this class and not others.

If you really need to process different error cases differently, you may provide more exception clauses:

try:
    ...
except ValueError:
    ...
except TypeError:
    ...

Sequences in except are also supported:

try:
    ...
except (ValueError, TypeError, ...):
    ...

+++ You should consider what does your code need to return if empty list was passed: -1? Raise an exception?

I think this is an example where you should not use an exception. While Python employs exception as "usual" programming constructs to reduce complexity, this is I think a case of adding complexity. Your code tells me that my_index has to hold a value after these lines and that you expect li to be an iterable, even under the occurrence of an exception. That this exception is ValueError which is the consequence of None not being in the list, is an added complexity, and one only understandable by convention - moreover, are you sure that its not IndexError that could be thrown? You see, IMHO the construct

if None in li: my_index = li.index(None)
else         : my_index = len(li) - 1

will not throw the programmer who comes across this code in half a year, ie maybe you yourself, off the track in understanding your program logic, while the version with the exception will cause additional mental work, raising the question if your anticipation of the possible exceptions and reactions was complete and correct. Only if this is a really really performance critical piece of code you should try to evade the double search.

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.

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