Подтвердить что ты не робот

Python, если vs try-except

Мне было интересно, почему try-except медленнее, чем if в программе ниже.

def tryway():
    try:
        while True:
            alist.pop()
    except IndexError:
        pass

def ifway():
    while True:
        if alist == []: 
            break
        else:
            alist.pop()
if __name__=='__main__':
    from timeit import Timer
    alist = range(1000)
    print "Testing Try"
    tr = Timer("tryway()","from __main__ import tryway")
    print tr.timeit()
    print "Testing If"
    ir = Timer("ifway()","from __main__ import ifway")
    print ir.timeit()

Результаты, которые я получаю, интересны.

Testing Try
2.91111302376
Testing If
0.30621099472

Может кто-нибудь пролить свет, почему попытка намного медленнее?

4b9b3361

Ответ 1

Вы устанавливаете alist только один раз. Первый вызов "tryway" очищает его, затем каждый последующий вызов ничего не делает.

def tryway():
    alist = range(1000)
    try:
        while True:
            alist.pop()
    except IndexError:
        pass

def ifway():
    alist = range(1000)
    while True:
        if alist == []:
            break
        else:
            alist.pop()
if __name__=='__main__':
    from timeit import Timer
    print "Testing Try"
    tr = Timer("tryway()","from __main__ import tryway")
    print tr.timeit(10000)
    print "Testing If"
    ir = Timer("ifway()","from __main__ import ifway")
    print ir.timeit(10000)

>>> Testing Try
>>> 2.09539294243
>>> Testing If
>>> 2.84440898895

Ответ 2

Обработка исключений на большинстве языков обычно медленная. Большинство компиляторов, интерпретаторов и виртуальных машин (которые поддерживают обработку исключений) рассматривают исключения (языковая идиома) как исключения (необычные). Оптимизация производительности включает компромиссы и быстрое выполнение исключений, как правило, будет означать, что другие области языка пострадают (либо в производительности, либо в простоте дизайна).

На более техническом уровне исключения обычно означают, что VM/интерпретатор (или исполняющая библиотека выполнения) должен сохранять кучу состояния и начать вытягивать все состояние в стеке вызовов функций (называемое разматыванием) до тех пор, пока точка, где найден действительный catch (except).

Или, глядя на него с другой точки зрения, программа перестает работать, когда возникает исключение и берет на себя "отладчик". Этот отладчик ищет обратно через стек (данные функции вызова) для catch, который соответствует исключению. Если он найдет один, он очистит вещи и вернет управление программе в этот момент. Если он не находит его, он возвращает управление пользователю (возможно, в форме интерактивного отладчика или REPL).

Ответ 3

Если вы действительно заинтересованы в скорости, оба ваших соперника могли бы похудеть.

while True: медленнее, чем while 1: - True является глобальной "переменной", которая загружается и тестируется; 1 является константой, а компилятор выполняет тест и испускает безусловный переход.

while True: избыточно. Сложите while/if/break вместе: while alist != []:

while alist != []: - медленный способ записи while alist:

Попробуйте следующее:

def tryway2():
    alist = range(1000)
    try:
        while 1:
            alist.pop()
    except IndexError:
        pass

def ifway2():
    alist = range(1000)
    while alist:
        alist.pop()

`

Ответ 4

Существует еще более быстрый способ повторения, хотя иногда мы хотим, чтобы список физически уклонялся, поэтому мы знаем, сколько осталось. Тогда alist должен быть параметром для генератора. (Джон также подходит для while alist:). Я ставлю функцию генератором и используемым списком (ifway()) и т.д., Поэтому значения действительны из функции (даже не используются):

def tryway():
    alist = range(1000)
    try:
        while True:
            yield alist.pop()
    except IndexError:
        pass

def whileway():
    alist = range(1000)
    while alist:
         yield alist.pop()

def forway():
    alist = range(1000)
    for item in alist:
         yield item

if __name__=='__main__':
    from timeit import Timer
    print "Testing Try"
    tr = Timer("list(tryway())","from __main__ import tryway")
    print tr.timeit(10000)
    print "Testing while"
    ir = Timer("list(whileway())","from __main__ import whileway")
    print ir.timeit(10000)
    print "Testing for"
    ir = Timer("list(forway())","from __main__ import forway")
    print ir.timeit(10000)

J:\test>speedtest4.py
Testing Try
6.52174983133
Testing while
5.08004508953
Testing for
2.14167694497

Ответ 5

Не уверен, но я думаю, что это что-то вроде этого: в то время как true соответствует обычной строке инструкции, что означает, что процессор может конвейерно и делать всевозможные приятные вещи. Исключения проходят прямо через все это, поэтому VM нужно обрабатывать его специально, и это требует времени.

Ответ 6

Просто подумал бросить это в микс: Я попробовал следующий ниже script, который, по-видимому, предполагает, что обработка исключения выполняется медленнее, чем обработка инструкции else:

import time

n = 10000000
l = range(0, n)
t0 = time.time()
for i in l:
    try:
        i[0]
    except:
        pass
t1 = time.time()
for i in l:
    if type(i) == list():
        print(i)
    else:
        pass
t2 = time.time()
print(t1-t0)
print(t2-t1)

дает:

5.5908801555633545
3.512694835662842

Итак, несмотря на то, что я знаю, что кто-то, скорее всего, будет комментировать использование time, а не timeit), похоже, на 60% замедляется использование try/except in loop. Таким образом, возможно, лучше пойти с if/else, пройдя цикл for из нескольких миллиардов элементов.

Ответ 7

защитное программирование требует, чтобы один тест на редкие и/или ненормальные условия, некоторые из которых в течение года или многих лет не произойдут, поэтому в этих обстоятельствах возможно попытка - кроме того, может быть оправдана.