1

I am currently writing a small python script. Is it ok to use break during an ExceptionError like the following ? I have 2 lists (a and b), and I'm trying to transfer one value from a to b every loop.

Here is the code:

while True:
    try:
        b.append(a.pop(0))
    except IndexError:
        print "a is empty !"
        break

    [...other code I don't wanna execute if I have an IndexError...]

Is it python-speaking "ok" to do like this ? I mean is it the best way in python to exit the loop inside of an ErrorException ?

Thanks for taking some time for it !

Romain Le Qllc
  • 856
  • 1
  • 6
  • 10
  • 1
    You'll get this error when `a` is empty. Beyond that, this is fine. – Patrick Haugh Oct 26 '16 at 18:47
  • Why not `while a:`? – Stefan Pochmann Oct 26 '16 at 18:48
  • 1
    @StefanPochmann I think this is technically faster. The try block has a lower overhead as long as no exceptions are being thrown, and that will only happen once, as opposed to the check every loop – Patrick Haugh Oct 26 '16 at 18:50
  • 1
    @PatrickHaugh I'd say the `pop(0)` suggests that speed isn't an issue here. – Stefan Pochmann Oct 26 '16 at 18:54
  • @romainwn "For sequences, (strings, lists, tuples), use the fact that empty sequences are false.": https://www.python.org/dev/peps/pep-0008/#programming-recommendations – Patrick Haugh Oct 26 '16 at 18:54
  • @StefanPochmann excellent point. I'm just saying that's probably where they've seen a similar pattern before – Patrick Haugh Oct 26 '16 at 18:54
  • 1
    @roganjosh http://stackoverflow.com/questions/1835756/using-try-vs-if-in-python – Patrick Haugh Oct 26 '16 at 18:55
  • @Patrick actually in the rest of the code I have another try-except block for another IndexError wich will use break aswell. Moreover, speed is actually an issue, but my question was really about the break inside the "except" Stefan : oh ok ! Anyway, thanks a lot guys for these answers ! – Romain Le Qllc Oct 26 '16 at 18:56
  • @romainwn I suggest you read the link i provided for roganjosh and make sure that try/except is the right way to go for your problem – Patrick Haugh Oct 26 '16 at 18:57
  • @PatrickHaugh I deleted my comment because I found that exact answer myself, sorry. I once tested `try/except` on a calculation that couldn't fail and it doubled execution time, so I need to look into this a bit more. – roganjosh Oct 26 '16 at 18:58
  • Why `b.append(a.pop(0))`? What's wrong with `b = a[:]`, or `b.extend(a)`? If you need to loop over the items in `a`, just use a `for` loop, eg `for u in a:` or `for i, u in enumerate(a):` – PM 2Ring Oct 26 '16 at 18:58
  • @PM2Ring there is some other code in the loop that we're not seeing that might be relevant – Patrick Haugh Oct 26 '16 at 18:59
  • 1
    @PatrickHaugh I tested it with a list of ten million ints and `while a:` was consistently significantly faster. See [code and output here](http://pastebin.com/VfrJuuwE). What am I doing wrong? – Stefan Pochmann Oct 26 '16 at 19:01
  • I insist on the fact that I'm focused on the break part, rather than getting the `b.append(a.pop(0))` part What I want to do is exiting the loop once a is empty, before executing the rest of the code – Romain Le Qllc Oct 26 '16 at 19:06
  • @romainwn: Yes, putting a `break` into an `except` clause is perfectly fine, as has already been mentioned. However, `b.append(a.pop(0))` is one of the most inefficient ways to copy a list, which is why we're discussing that. `.pop(0)` forces all of the remaining list items to be moved. Sure, that happens at C speed, but it's still best to avoid it unless you really need it. – PM 2Ring Oct 26 '16 at 19:09
  • what if i'm using a deque instead ? it would be `a = deque()` with all the values, and use `b.append(a.popleft())` instead ? – Romain Le Qllc Oct 26 '16 at 19:16
  • A deque is designed to be efficient to add or remove items at either end, a list isn't. – PM 2Ring Oct 26 '16 at 19:18
  • @StefanPochmann I think it's case-by-case. [This](http://pastebin.com/ZKJDGHqR) test I did comes out with everything pretty much level (takes ~100 secs to run, and in Python 2.7). – roganjosh Oct 26 '16 at 19:35
  • @roganjosh That's because you're hiding its small speed difference between the huge time taken by the unreasonable `pop(0)`. Better use `pop()` and larger sizes. Though your `for y in xrange(length)` isn't really comparable to `while a:` anyway, since it for example doesn't deal with `a` being extended or shrunk in the rest of the loop. – Stefan Pochmann Oct 26 '16 at 20:00
  • @PatrickHaugh I [*added another version*](http://pastebin.com/78NJFAv5), with `try` *outside* the `while`, and it's still slower. – Stefan Pochmann Oct 26 '16 at 20:05
  • @StefanPochmann Not that I want to disagree with you, you have a place in my mind as the optimisation guy :) But the test you gave me back is very volatile ([`1.90699982643 2.28600001335 1.80400013924], [1.60700011253 1.82999992371 2.04399991035], [1.77300000191 1.87300014496 1.75899982452]`). – roganjosh Oct 26 '16 at 20:07
  • @StefanPochmann I can't access pastebin at the moment, but my tests have them running roughly even. I think that this is interesting enough behavior to warrant its own question, especially since it seems to contradict the link i posted earlier – Patrick Haugh Oct 26 '16 at 20:08
  • @StefanPochmann ok, I had a `multiprocessing` script running at the same time that I forgot about. Yes, `while a:` does appear to win each time here. – roganjosh Oct 26 '16 at 20:24
  • @StefanPochmann this might be easier in chat than here. But `pop()` and `pop(0)` are very different; `pop()` will take from the end of the list. The OP wants to `pop(0)` so the only fair comparison is with `pop(0)`. While your example shows a break-away winner, it doesn't for `pop(0)` as my test showed. – roganjosh Oct 26 '16 at 20:34
  • @roganjosh No, comparing with `pop(0)` doesn't make sense (and the OP really shouldn't be using it anyway, unless the lists are tiny and it's somehow clearer code). If you do, then you're really testing the variance of `pop(0)` and your system, not the different looping ways. And then why are you doing it at all? – Stefan Pochmann Oct 26 '16 at 20:41
  • @StefanPochmann with the information available then that is our benchmark :) But, given our conversation here, I think there is scope for an efficient answer with a little more detail from OP – roganjosh Oct 26 '16 at 20:45
  • 1
    @PatrickHaugh You mean the measurements of the accepted answer there? I don't trust them. Times around 0.1 seconds are no good. He should have used a much higher number of iterations. I'd say `a=1;b=1` with `try:\n a/b\nexcept ZeroDivisionError:\n pass` and `if b!=0:\n a/b"` are the ones most relevant for us, right? With the default 10^6 iterations, the `if` mostly wins for me, sometimes by a lot because the `try` version is pretty unstable. With 10^8, both are pretty stable around 4.8 seconds, can't tell which is faster. But with `if b` instead of `if b!=0` it's stable around 4.55 seconds. – Stefan Pochmann Oct 26 '16 at 20:57

2 Answers2

3

As a general principle, yes, it's okay to do this. However, it's far better to make a check as simple as this in the while loop's condition:

while a:
    b.append(a.pop(0))
Prune
  • 76,765
  • 14
  • 60
  • 81
0

Its totally fine doing this way ! :)

ExPlOiD
  • 153
  • 2
  • 13