Самый эффективный способ сделать утверждение if-elif-elif-else, когда остальное сделано больше всего?
У меня есть оператор in if-elif-elif-else, в котором в 99% случаев выполняется оператор else:
if something == 'this':
doThis()
elif something == 'that':
doThat()
elif something == 'there':
doThere()
else:
doThisMostOfTheTime()
эта конструкция делается большое, но поскольку она идет за каждое условие, прежде чем она попадает еще мне кажется, что это не очень эффективно, не говоря уже весть. С другой стороны, он должен знать, выполняются ли какие-либо из этих условий, поэтому он должен проверить его в любом случае.
кто-нибудь знает, если и как это можно сделать больше эффективно или это просто лучший способ сделать это?
4 ответа:
код...
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]
...и обратите внимание на количество процессорного времени, которое они используют...
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)()
если
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
is самый эффективный способ здесь: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]
это выход, но не может быть наиболее подходящие для Python способ сделать это, потому что это менее читаемо, для которых не владеет в Python.