99 %의 시간 동안 else 문이 실행되는 if-elif-elif-else 문이 있습니다.
if something == 'this':
doThis()
elif something == 'that':
doThat()
elif something == 'there':
doThere()
else:
doThisMostOfTheTime()
이 구조는 많이 수행 되지만 다른 조건에 도달하기 전에 모든 조건을 검토하기 때문에 Pythonic은 말할 것도없고 매우 효율적이지 않다고 느낍니다. 반면에 이러한 조건이 충족되는지 알 필요가 있으므로 어쨌든 테스트해야합니다.
이것이 더 효율적으로 수행 될 수 있는지, 아니면 이것이 가능한 최선의 방법인지 아는 사람이 있습니까?
답변
코드…
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을 사용합니다. 원래 구조.
답변
나는 사전을 만들 것이다.
options = {'this': doThis,'that' :doThat, 'there':doThere}
이제 다음을 사용하십시오.
options.get(something, doThisMostOfTheTime)()
dict에 something
없는 경우 기본값을 반환합니다.options
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()
답변
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
답변
다음은 동적 조건이 사전으로 번역 된 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]
그것은 방법이지만 파이썬에 능숙하지 않은 사람이 읽기가 어렵 기 때문에 가장 비단뱀적인 방법은 아닐 수 있습니다.
답변
사람들 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])