3

I add a lot of elements to a list of lists. If the list of lists my element should be saved to, does not exist yet, I handle this by catching an exception and adding a new list to my list of lists.

The reason I do this instead of checking every time, if the specified list already exists, is, that I add thousands of elements, to just a few lists. So the case, that the list was not created yet, occurs only a few times. Thus I think it is faster to just raise an exception and handle it, instead of checking list lengths thousands of times.

Here is an example of what I mean:

try:
    self.layers[pos].extend(node)
except IndexError:
    self.layers.append([])
    self.layers[pos].extend(nodes)

(I know the index pos can not be higher than the length of layers. Thus appending one if an error occurs is always enough)

So back to my question, is that bad coding practice, or is it okay to do it like this for the sake of performance?

I found this discussion about it, but performance was not a topic there.

3
  • 2
    ""I think it is faster" - so you just guess and did not measure? If that's true, let me make a guess: your guess is probably wrong. Oh, now I read your comment to that other answer, saying you already proved your guess was wrong - so what is the point of the question now?
    – Doc Brown
    Commented May 14, 2016 at 22:18
  • Possible duplicate of Is micro-optimisation important when coding?
    – gnat
    Commented May 15, 2016 at 6:38
  • 1
    @gnat -- Yes, this possibly is a duplicate, but not in the way you think. The code is obviously Python. If the concern was efficiency, the OP should have asked if it's more efficient to use an if/else statement as opposed to a try/catch. Since testing performed by the OP himself said there is no measurable difference, the right thing to do is to use follow the idiomatic approach for that language. In the case of Python, that would be the code as expressed in the question. Using an if/else statement to ask for permission is anti-pythonic. Commented May 15, 2016 at 17:11

5 Answers 5

0

Yes this is a bad coding practice, since it obfuscates what is going on.

Worse, since throwing an exception is such an expensive operation in pretty much every language, this won't even get you anything performance-wise! (Though I would measure to make sure)

5
  • I checked the time, and for various cases, the time was more or less the same. My test program was written in python.
    – Natjo
    Commented May 14, 2016 at 21:50
  • @Jonas: Common sense would seem to suggest that you don't make an architectural change (especially a controversial one) when it is not going to matter. Commented May 14, 2016 at 22:17
  • 1
    This is a good answer for an LBYL language, but it is not a good answer for Python. Asking for forgiveness is very pythonic. Since the code is in python and since the testing by the OP says there isn't much difference in performance, the best thing to do is to write the code in the way most comprehensible to people familiar with the language (you don't want to maintain that code forever, do you, @Jonas?) Commented May 15, 2016 at 14:57
  • I can spot code that is supposedly C++ but is actually C plus or minus at a glance because C programmers do not use C++ idioms. I can similarly spot code that is supposedly C but is actually C plus or minus at a glance because C++ programmers do not use C idioms. I can also spot at a glance code the is supposedly Python but is actually C-python (or C++-python, or Java-python, or Cobol-python) because those programmers do not use Python idioms. To be properly multilingual means one needs to know the idioms of the language one is using and be able to think in terms of those idioms. Commented May 15, 2016 at 15:03
  • 2
    In short, this is not a good answer for Python code, and this is the reason I downvoted this accepted answer. Commented May 15, 2016 at 15:04
7

Using exceptions for flow control is highly frowned upon in most languages, but not in Python. Using exceptions for flow control in Python is "pythonic." Exceptions are at the heart of how python for loops work, which terminate the iteration on receiving a StopIteration exception. Python has a slightly derogatory term for languages such as C++, Java, and C# where programmers are strongly advised to *never* use exceptions for flow control. These are "Look Before You Leap" (LBYL) languages.

Python on the other hand is an EAFP language: It's Easier to Ask for Forgiveness than Permission. Using exceptions for flow control is at the very heart of the language. Your code is nicely pythonic.

That said, if you find yourself repeating yourself in multiple places, you might want to follow the DRY (don't repeat yourself) principle and create a list of lists (LoL) (or perhaps a defaultlist class and thereby avoid my dry sense of humor) that extends the builtin list class. You'll want to override the __setitem__ method (and possibly the __getitem__ method) so that that missing elements are filled in with an empty list. Then you can append at will, and you won't even have to use a try / except block. Now your code will follow the "Nike" principle: Just do it.

3
  • There is really an abbreviation for everything in coding! Not sure how much of those was made up by you. Thanks anyway, I actually checked the runtime for my algorithm in python, and exception handling still slows the program vastly down in comparison to if queries.
    – Natjo
    Commented May 15, 2016 at 13:28
  • 2
    @Jonas - None of this was made up by me. Check the Python glossary, where you can find discussions on EAFP and LBYL and their significance to Python. Multiple people have independently come up with the Nike principle of programming ("Just do it!"). I've used that term myself for over a decade, but I doubt I invented it. Commented May 15, 2016 at 14:20
  • Also see What is the EAFP principle in Python? at stackoverflow.com. Commented May 15, 2016 at 14:29
2

Python has a builtin method setdefault which does exactly what you need:

self.layers.setdefault("pos", []).extend(node)
1
  • This question is about lists of lists. There is no setdefault method for a list. Commented May 14, 2016 at 23:56
0

I hear this misguided idea a lot: "exceptions are situations in your code that you should never reach. By their name, they suggest exceptional, unexpected and uncontrollable situations. Throwing an exception should stop the execution of your code all together."

This is misguided because, suppose you want to access a file, and the file cannot be accessed because it is locked, so a "File is locked" exception is thrown; what are you going to do? Stop the program? If you are writing a GUI application, the only reasonable thing to do is to display a "retry, cancel" dialog, meaning catching the exception and taking action on it.

The quote is actually dangerous because it may prompt the novice programmer to do something like if (not resource_is_locked) access_resource which will of course appear to work, but will fail under seemingly random circumstances under race conditions.

The only part of the above quote that is true is that exceptions suggest exceptional situations. From the moment you catch the exception it becomes an expected situation, and from the moment you put any code in the catch block it is a controlled situation.

So, it is perfectly fine to define a situation as "exceptional" and handle it with an exception. I would not call it flow control. I would just call it exception handling.

Just be sure to a) first exhaust all other possibilities of doing it elegantly without exceptions, and b) if you must do it using exceptions, then clearly document what is going on.

Also keep in mind that in Windows with Dotnet, throwing an exception is terribly expensive due to the twisted way microsoft has implemented things. In Java, either in Windows or in Linux, throwing and catching an exception is not terribly expensive. (Though still more expensive than it ought to be, in my opinion.)

6
  • 1
    I've never heard the idea expounded in the first paragraph. I have heard "use exceptions for exceptional conditions," but a file lock is an exceptional condition. I say it as "Use exceptions for situations that the program cannot reasonably recover from without user intervention," which generally covers file locks, "file not found," protocol errors and so forth. Commented May 14, 2016 at 21:25
  • @RobertHarvey the quote is from the very first comment in the discussion linked by the OP.
    – Mike Nakis
    Commented May 14, 2016 at 21:28
  • Eh, I see why you thought that, but I don't really think that's what he's saying. He's performing a micro-optimization by using an exception to avoid checking some condition every time through a loop. Your characterization doesn't really say that. Commented May 14, 2016 at 21:30
  • @RobertHarvey I am just trying to warn the OP against blindly following maxims. If you try for a moment to think outside the box, an exception signifies nothing but an exceptional situation. So, whether an exception is suitable or not to handle a given situation depends entirely upon whether you label the situation exceptional or not.
    – Mike Nakis
    Commented May 14, 2016 at 21:31
  • 1
    This idea comes up a lot with respect to Python, and in Python, it is indeed a misguided idea (plus one). If you don't like that feature of that language, there's a simple solution: Walk away. There are lots and lots of other languages out there one can use in lieu of Python. Commented May 15, 2016 at 17:00
0

First of all, as long as you don't know that the piece of code in question actually is a performance bottleneck, don't bother optimizing it and write it in the most clear, straightforward way that you can think of.


Whether the exception is faster than the explicit check depends entirely on the language/compiler. To be more precise, there are two points involved:

  1. To throw the exception, the machine code must encode an implicit check. If you encode an explicit check yourself, that duplicates the amount of checks that are done. This should indeed slow down your code.

  2. Throwing an exception is exceptionally expensive in many languages. It may require a huge amount of avoided double checks to make up for a single exception that's thrown. This may easily make your optimized code run significantly slower.

It depends entirely on your compiler whether it is able to optimize away the implicit check controlling the exception if you write an explicit check for the same condition into your code. So, if you compiler is smart enough, the explicit check will always be faster. However, if your compiler is stupid but handles exception in a quick fashion, the implicit exception based check is faster.

As such, truth is only in measurement. Implement both versions, run them through a realistic performance test, measure their execution times, and compare. Anything else is just guesswork.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.