问题:当else做得最多时,最有效的方式来执行if-elif-elif-else语句?
我有一条if-elif-elif-else语句,其中99%的时间执行else语句:
if something == 'this':
doThis()
elif something == 'that':
doThat()
elif something == 'there':
doThere()
else:
doThisMostOfTheTime()
这个构造完成了很多,但是由于它在碰到其他情况之前会遍历所有条件,所以我觉得这不是很有效,更不用说Pythonic了。另一方面,它确实需要知道是否满足任何这些条件,因此无论如何都应该对其进行测试。
是否有人知道是否以及如何可以更有效地完成此工作,或者这仅仅是做到这一点的最佳方法?
回答 0
代码…
options.get(something, doThisMostOfTheTime)()
…看起来应该是快,但它实际上是慢于if
… elif
… else
结构,因为它要调用一个函数,它可以在一个紧密的循环一个显著的性能开销。
考虑这些例子…
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]
…并注意他们使用的CPU时间…
1.py: 160ms
2.py: 170ms
3.py: 110ms
4.py: 100ms
…使用来自的用户时间time(1)
。
选项#4确实有额外的内存开销,需要为每个不同的键缺失添加一个新项,因此,如果您期望数量众多的不同的键缺失,我会选择方法#3,它在原始构造。
回答 1
我要创建一个字典:
options = {'this': doThis,'that' :doThat, 'there':doThere}
现在只使用:
options.get(something, doThisMostOfTheTime)()
如果something
在options
dict中找不到,dict.get
则将返回默认值doThisMostOfTheTime
一些时间比较:
脚本:
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()
结果:
>>> 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
对于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
因此,对于普通字典而言,在key in options
这里使用键是最有效的方法:
if key in options:
options[key]()
else:
doSomethingElse()
回答 2
可以使用pypy吗?
保留原始代码,但在pypy上运行可使我的速度提高50倍。
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
回答 3
这里是将动态条件转换为字典的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]
这是一种方法,但可能不是最Python的方法,因为对于不熟练使用Python的人来说可读性较差。
回答 4
人们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])
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。