Эффективность оператора Python

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

if ( a and b and c): do something 

утверждения a, b, c все проверяли их равенство или отсутствие заданных значений. Когда я посмотрел на него, я обнаружил, что из-за характера тестов я мог бы переписать весь блок логики на две ветви, которые никогда не превышали 3 глубины и редко проходили мимо первого уровня (что делает самый редкий случайный тест первый).

 if a: if b: if c: else: if c: else: if b: if c: else: if c: 

Для меня логически кажется, что он должен быть быстрее, если вы делаете меньше, более простые тесты, которые не работают быстрее и продвигаются дальше. Мои реальные вопросы:

1) Когда я говорю if и else, должен ли быть true, может ли другое проигнорировать полностью?

2) Теоретически

если (a и b и c)

возьмите столько времени, сколько три отдельных заявления if?

8 Solutions collect form web for “Эффективность оператора Python”

if инструкции будут пропускать все в скобке else если она будет равна true. Следует отметить, что беспокоиться об этой проблеме, если она не выполняется миллионы раз за выполнение программы, называется «преждевременной оптимизацией», и ее следует избегать. Если ваш код более четкий с тремя операциями if (a and b and c) , они должны быть оставлены.

Я бы сказал, что один тест проходит так же быстро, как и отдельные тесты. Python также использует так называемую оценку короткого замыкания .

Это означает для (a and b and c) , что b или c больше не будут проверяться, если a является false .

Аналогично, если у вас есть выражение OR (a or b) и a true , b никогда не оценивается.

Итак, чтобы подвести итог, статьи не ускоряются с разделением.

Код:

 import dis def foo(): if ( a and b and c): pass else: pass def bar(): if a: if b: if c: pass print 'foo():' dis.dis(foo) print 'bar():' dis.dis(bar) 

Вывод:

 foo(): 4 0 LOAD_GLOBAL 0 (a) 3 JUMP_IF_FALSE 18 (to 24) 6 POP_TOP 7 LOAD_GLOBAL 1 (b) 10 JUMP_IF_FALSE 11 (to 24) 13 POP_TOP 14 LOAD_GLOBAL 2 (c) 17 JUMP_IF_FALSE 4 (to 24) 20 POP_TOP 5 21 JUMP_FORWARD 1 (to 25) >> 24 POP_TOP 7 >> 25 LOAD_CONST 0 (None) 28 RETURN_VALUE bar(): 10 0 LOAD_GLOBAL 0 (a) 3 JUMP_IF_FALSE 26 (to 32) 6 POP_TOP 11 7 LOAD_GLOBAL 1 (b) 10 JUMP_IF_FALSE 15 (to 28) 13 POP_TOP 12 14 LOAD_GLOBAL 2 (c) 17 JUMP_IF_FALSE 4 (to 24) 20 POP_TOP 13 21 JUMP_ABSOLUTE 29 >> 24 POP_TOP 25 JUMP_ABSOLUTE 33 >> 28 POP_TOP >> 29 JUMP_FORWARD 1 (to 33) >> 32 POP_TOP >> 33 LOAD_CONST 0 (None) 36 RETURN_VALUE 

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

По крайней мере, в python эффективность является второй по сравнению с читабельностью, а «Flat лучше, чем вложен».

См. «Дзен Питона»

Я сомневаюсь, что вы увидите измеримую разницу, поэтому я бы рекомендовал делать все, что делает код наиболее читаемым.

Если вы обеспокоены тем, что b или c – это функции, которые вызывается вместо переменных, которые оцениваются, то этот код показывает, что короткое замыкание является вашим другом:

 a = False def b(): print "b was called" return True if a and b(): print "this shouldn't happen" else: print "if b was not called, then short-circuiting works" 

печать

 if b was not called, then short-circuiting works 

Но если у вас есть код, который делает это:

 a = call_to_expensive_function_A() b = call_to_expensive_function_B() c = call_to_expensive_function_C() if a and b and c: do something... 

то ваш код по-прежнему вызывает все 3 дорогие функции. Лучше позволить Python быть Python:

 if (call_to_expensive_function_A() and call_to_expensive_function_B() and call_to_expensive_function_C()) do something... 

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

редактировать

Вы можете обобщить это, используя all встроенные:

 # note, this is a list of the functions themselves # the functions are *not* called when creating this list funcs = [function_A, function_B, function_C] if all(fn() for fn in funcs): do something 

Теперь, если вам нужно добавить другие функции или изменить их порядок (возможно, function_A занимает очень много времени, и вам будет полезно, если вы будете фильтровать случаи, которые сначала не сбой function_B или function_C ), вы просто обновляете список funcs . all делает короткое замыкание так же, как если бы вы изложили if, как if a and b and c . (Если функции 'or'ed вместе, используйте any встроенный вместо этого.)

if (a and b and c) сбой, если a является ложным, а не проверяет b или c .

Тем не менее, я лично считаю, что вложенные условности легче читать, чем 2 ^ n комбинаций условностей.

В общем, если вы хотите определить, какой способ сделать что-то быстрее, вы можете написать простой тест с использованием timeit .

if (a and b and c) быстрее и лучше, ради оптимизации кода реального программиста и читаемости кода.

  • Почему обрабатывается отсортированный массив не быстрее, чем несортированный массив в Python?
  • Необычная разница в скорости между Python и C ++
  • Эффективность индексирования списка Python
  • Сопоставление памяти замедляется со временем, альтернативы?
  • Параллельные запросы в Appengine Python
  • Python - Самый эффективный способ реализовать два дополнения?
  • Производительность NumPy: uint8 против float и умножения или деления?
  • Запрос и базовая информация профилирования для Flask
  • Каков наилучший способ создания всех возможных трех буквенных строк?
  • Самый быстрый способ поменять элементы в списке Python
  • Производительность NLTK
  •  
    Interesting Posts for Van-Lav
    Python - лучший язык программирования в мире.