Python 当 else 完成最多时,制作 if-elif-elif-else 语句的最有效方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17166074/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Most efficient way of making an if-elif-elif-else statement when the else is done the most?
提问by kramer65
I've got a in if-elif-elif-else statement in which 99% of the time, the else statement is executed:
我有一个 in if-elif-elif-else 语句,其中 99% 的时间执行 else 语句:
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.
这个构造做了很多,但是因为它在遇到 else 之前遍历了所有条件,所以我觉得这不是很有效,更不用说 Pythonic 了。另一方面,它确实需要知道是否满足这些条件中的任何一个,因此无论如何它都应该对其进行测试。
Does anybody know if and how this could be done more efficiently or is this simply the best possible way to do it?
有谁知道这是否以及如何更有效地完成,或者这只是最好的方法吗?
采纳答案by Aya
The code...
编码...
options.get(something, doThisMostOfTheTime)()
...looks like it ought to be faster, but it's actually slower than the if... elif... elseconstruct, because it has to call a function, which can be a significant performance overhead in a tight loop.
...看起来它应该更快,但实际上它比if... elif...else构造慢,因为它必须调用一个函数,这在紧密循环中可能是一个显着的性能开销。
Consider these examples...
考虑这些例子...
1.py
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
2.py
something = 'something'
options = {'this': 1, 'that': 2, 'there': 3}
for i in xrange(1000000):
the_thing = options.get(something, 4)
3.py
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
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...
...并注意他们使用的 CPU 时间量...
1.py: 160ms
2.py: 170ms
3.py: 110ms
4.py: 100ms
...using the user time from time(1).
...使用用户时间从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.
选项 #4 确实有额外的内存开销,为每个不同的键未命中添加一个新项目,所以如果您期望无限数量的不同键未命中,我会选择选项 #3,这仍然是一个显着的改进原始构造。
回答by Ashwini Chaudhary
I'd create a dictionary :
我会创建一个字典:
options = {'this': doThis,'that' :doThat, 'there':doThere}
Now use just:
现在只使用:
options.get(something, doThisMostOfTheTime)()
If somethingis not found in the optionsdict then dict.getwill return the default value doThisMostOfTheTime
如果something在options字典中没有找到,则dict.get返回默认值doThisMostOfTheTime
Some timing comparisons:
一些时序比较:
Script:
脚本:
from random import shuffle
def doThis():pass
def doThat():pass
def doThere():pass
def doSomethingElse():pass
options = {'this':doThis, 'that':doThat, 'there':doThere}
lis = range(10**4) + options.keys()*100
shuffle(lis)
def get():
for x in lis:
options.get(x, doSomethingElse)()
def key_in_dic():
for x in lis:
if x in options:
options[x]()
else:
doSomethingElse()
def if_else():
for x in lis:
if x == 'this':
doThis()
elif x == 'that':
doThat()
elif x == 'there':
doThere()
else:
doSomethingElse()
Results:
结果:
>>> from so import *
>>> %timeit get()
100 loops, best of 3: 5.06 ms per loop
>>> %timeit key_in_dic()
100 loops, best of 3: 3.55 ms per loop
>>> %timeit if_else()
100 loops, best of 3: 6.42 ms per loop
For 10**5non-existent keys and 100 valid keys::
对于10**5不存在的键和 100 个有效键:
>>> %timeit get()
10 loops, best of 3: 84.4 ms per loop
>>> %timeit key_in_dic()
10 loops, best of 3: 50.4 ms per loop
>>> %timeit if_else()
10 loops, best of 3: 104 ms per loop
So, for a normal dictionary checking for the key using key in optionsis the most efficient way here:
因此,对于普通字典检查使用的键key in options是最有效的方法:
if key in options:
options[key]()
else:
doSomethingElse()
回答by foz
Are you able to use pypy?
你可以使用pypy吗?
Keeping your original code but running it on pypy gives a 50x speed-up for me.
保留您的原始代码但在 pypy 上运行它对我来说速度提高了 50 倍。
CPython:
CPython:
matt$ python
Python 2.6.8 (unknown, Nov 26 2012, 10:25:03)
[GCC 4.2.1 Compatible Apple Clang 3.0 (tags/Apple/clang-211.12)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> from timeit import timeit
>>> timeit("""
... if something == 'this': pass
... elif something == 'that': pass
... elif something == 'there': pass
... else: pass
... """, "something='foo'", number=10000000)
1.728302001953125
Pypy:
派皮:
matt$ pypy
Python 2.7.3 (daf4a1b651e0, Dec 07 2012, 23:00:16)
[PyPy 2.0.0-beta1 with GCC 4.2.1] on darwin
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``a 10th of forever is 1h45''
>>>>
>>>> from timeit import timeit
>>>> timeit("""
.... if something == 'this': pass
.... elif something == 'that': pass
.... elif something == 'there': pass
.... else: pass
.... """, "something='foo'", number=10000000)
0.03306388854980469
回答by Arthur Juli?o
That's an example of a if with dynamic conditions translated to a dictionary.
这是一个将动态条件转换为字典的 if 示例。
selector = {lambda d: datetime(2014, 12, 31) >= d : 'before2015',
lambda d: datetime(2015, 1, 1) <= d < datetime(2016, 1, 1): 'year2015',
lambda d: datetime(2016, 1, 1) <= d < datetime(2016, 12, 31): 'year2016'}
def select_by_date(date, selector=selector):
selected = [selector[x] for x in selector if x(date)] or ['after2016']
return selected[0]
It is a way, but may not be the most pythonic way to do it because is less readable for whom is not fluent in Python.
这是一种方法,但可能不是最 Pythonic 的方法,因为对于不熟悉 Python 的人来说可读性较差。
回答by user3319934
People warn about execfor security reasons, but this is an ideal case for it.
It's an easy state machine.
人们exec出于安全原因发出警告,但这是一个理想的情况。
这是一个简单的状态机。
Codes = {}
Codes [0] = compile('blah blah 0; nextcode = 1')
Codes [1] = compile('blah blah 1; nextcode = 2')
Codes [2] = compile('blah blah 2; nextcode = 0')
nextcode = 0
While True:
exec(Codes[nextcode])

