Most efficient way of making an if-elif-elif-else statement when the else is done the most?
我有一个in-if-elif-elif-else语句,其中99%的时间执行else语句:
1 2 3 4 5 6 7 8 | if something == 'this': doThis() elif something == 'that': doThat() elif something == 'there': doThere() else: doThisMostOfTheTime() |
这个构造做了很多,但是因为它在碰到其他东西之前就已经克服了所有的条件,所以我觉得这不是很有效,更不用说Python了。另一方面,它需要知道是否满足这些条件中的任何一个,因此它无论如何都应该测试它。
有人知道这是否以及如何能更有效地做到,或者这仅仅是最好的方法吗?
代码…
1 | options.get(something, doThisMostOfTheTime)() |
…看起来应该更快,但实际上比
考虑这些例子…
1.Py
1 2 3 4 5 6 7 8 9 10 11 | 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
1 2 3 4 5 | something = 'something' options = {'this': 1, 'that': 2, 'there': 3} for i in xrange(1000000): the_thing = options.get(something, 4) |
3.Py
1 2 3 4 5 6 7 8 | 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
1 2 3 4 5 6 7 | 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] |
…并注意它们使用的CPU时间量…
1 2 3 4 | 1.py: 160ms 2.py: 170ms 3.py: 110ms 4.py: 100ms |
…使用
选项4确实有额外的内存开销,可以为每个不同的键未命中添加一个新项,因此,如果您希望有无限数量的不同键未命中,我将使用选项3,这仍然是对原始构造的一个显著改进。
我会创建一本字典:
1 | options = {'this': doThis,'that' :doThat, 'there':doThere} |
现在只使用:
1 | options.get(something, doThisMostOfTheTime)() |
如果
一些时间比较:
脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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() |
结果:
1 2 3 4 5 6 7 | >>> 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 |
对于
1 2 3 4 5 6 | >>> %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 |
因此,对于普通字典来说,使用
1 2 3 4 | if key in options: options[key]() else: doSomethingElse() |
你能用派比吗?
保留您的原始代码,但在pypy上运行它会使我的速度提高50倍。
CPython:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
这是一个将动态条件转换为字典的if示例。
1 2 3 4 5 6 7 | 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] |
这是一种方法,但可能不是最适合用Python的方法,因为对于不精通Python的人来说,它的可读性较低。