I've got a in if-elif-elif-else statement in which 99% of the time, the else statement is executed:
if something == 'this': doThis() elif something == 'that': doThat() elif something == 'there': doThere() else: doThisMostOfTheTime()
This construct is done a lot, but since it goes over every condition before it hits the else I have the feeling this is not very efficient, let alone Pythonic. On the other hand, it does need to know if any of those conditions are met, so it should test it anyway.
Does anybody know if and how this could be done more efficiently or is this simply the best possible way to do it?
so the difference is that the code always checks to see if an 'if' statement is true, checks 'elif' statements only if each 'if' and 'elif' statement above it is false, and 'else' runs only when the conditions for all attached 'if' and 'elif' statements are false.
The elif is short for else if. It allows us to check for multiple expressions. If the condition for if is False , it checks the condition of the next elif block and so on. If all the conditions are False , the body of else is executed.
Use the elif condition is used to include multiple conditional expressions after the if condition or between the if and else conditions. The elif block is executed if the specified condition evaluates to True .
The code...
options.get(something, doThisMostOfTheTime)()
...looks like it ought to be faster, but it's actually slower than the if
... elif
... else
construct, because it has to call a function, which can be a significant performance overhead in a tight loop.
Consider these examples...
1.py
something = 'something' for i in xrange(1000000): if something == 'this': the_thing = 1 elif something == 'that': the_thing = 2 elif something == 'there': the_thing = 3 else: the_thing = 4
2.py
something = 'something' options = {'this': 1, 'that': 2, 'there': 3} for i in xrange(1000000): the_thing = options.get(something, 4)
3.py
something = 'something' options = {'this': 1, 'that': 2, 'there': 3} for i in xrange(1000000): if something in options: the_thing = options[something] else: the_thing = 4
4.py
from collections import defaultdict something = 'something' options = defaultdict(lambda: 4, {'this': 1, 'that': 2, 'there': 3}) for i in xrange(1000000): the_thing = options[something]
...and note the amount of CPU time they use...
1.py: 160ms 2.py: 170ms 3.py: 110ms 4.py: 100ms
...using the user time from time(1)
.
Option #4 does have the additional memory overhead of adding a new item for every distinct key miss, so if you're expecting an unbounded number of distinct key misses, I'd go with option #3, which is still a significant improvement on the original construct.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With