0

Couldn't find much information on try vs. if when you're checking more than one thing. I have a tuple of strings, and I want to grab an index and store the index that follows it. There are 2 cases.

mylist = ('a', 'b')

or

mylist = ('y', 'z')

I want to look at a or y, depending on which exists, and grab the index that follows. Currently, I'm doing this:

item['index'] = None
try:
    item['index'] = mylist[mylist.index('a') + 1]
except ValueError:
    pass
try:
    item['index'] = mylist[mylist.index('y') + 1]
except ValueError:
    pass

After reading Using try vs if in python, I think it may actually be better/more efficient to write it this way, since half the time, it will likely raise an exception, (ValueError), which is more expensive than an if/else, if I'm understanding correctly:

if 'a' in mylist:
    item['index'] = mylist[mylist.index('a') + 1]
elif 'y' in mylist:
    item['index'] = mylist[mylist.index('y') + 1]
else:
    item['index'] = None

Am I right in my assumption that if is better/more efficient here? Or is there a better way of writing this entirely that I'm unaware of?

Community
  • 1
  • 1
Jer_TX
  • 465
  • 4
  • 20

3 Answers3

1

Your first code snippet should rather be like below:

try:
    item['index'] = mylist[mylist.index('a') + 1]
except ValueError:
    try:
        item['index'] = mylist[mylist.index('y') + 1]
    except ValueError:
        item['index'] = None

or have a for loop like this:

for element in ['a', 'y']:
    try:
        item['index'] = mylist[mylist.index(element) + 1]
    except ValueError:
        continue
    else:
        break
else:
    item['index'] = None

IMO using try blocks is better performance-wise as you can avoid if-checking in every case irrespective of how often the ValueError occurs or not and its also more readable and pythonic.

rohithvsm
  • 94
  • 5
  • The `for` loop certainly looks cleaner to me, +1. So, even though it will raise `ValueError` likely half the time, you think it's still faster to use `try` instead of `if`? – Jer_TX Jul 22 '15 at 18:20
  • I personally always use try-except. But if you think its truly not an exceptional condition, but a norm. Maybe you just want to write both the versions and time it from the command-line like $time python your_script.py. It is pretty easy to check this way than to use timeit within the program. – rohithvsm Jul 22 '15 at 18:25
1

Well my opinion is close to Rohith Subramanyam's. I think a for loop is definitely more readable, especially in the case where you have more than two elements to test !

But I still think an if block is far more logical to use (pun intended!). It is also strictly speaking less redundant in terms of lines of code:

accepted_elements = ['a', 'y']
item['index'] = None
for accepted_element in accepted_elements:
    if accepted_element in mylist:
        item['index'] = mylist[mylist.index(accepted_element) + 1]
        break

I think the solution you use in the end is really up to you, as it depends on your code habits (except the for loop which is a must).

EDIT: Well actually, after some time measurements it seems that Rohith Subramanyam's version is slightly faster at first sight. (640 ns per loop vs 740 ns per loop)

Challensois
  • 522
  • 2
  • 10
  • Your answer seems cleaner to me, and I believe my initial assumption of using `if` is faster. (btw, `element` in your last line should be `accepted_element` :P) – Jer_TX Jul 22 '15 at 18:36
  • 1
    I linked that question in my question. My main question was asking about multiple tries knowing they will raise exceptions, or using ifs, and if the rule still applies. If so, `if` would be faster since exceptions are expensive. – Jer_TX Jul 22 '15 at 18:50
0

Performance-wise, exceptions in Python are not heavy like in other languages and you shouldn't worry about using them whenever it's appropriate.

That said, IMO the second code snippet is more concise and clean.

Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
  • Well, this is part of a web crawler/scraper. so I would assume that once I have more complex code or just simply more code, anywhere I can save half a second or so would be pretty beneficial. According to the question I linked, using the `try` that ends in an exception takes up half a second over the `if` – Jer_TX Jul 22 '15 at 18:07
  • @JeremyDavis `try` is not "half a second" over `if`, it's half a second over `if` over a million cycles of running the code. Look at [timeit](https://docs.python.org/2/library/timeit.html#timeit.Timer.timeit) doc, look for the `number` argument - as you can see it defaults to one million. – Nir Alfasi Jul 22 '15 at 19:21