Условные выражения: not x или x == False?
В python существует два варианта проверки отрицательного условия:
if not x:
if x == False:
Какой из этих двух вариантов лучше на предмет удобочитаемости и/или быстродействия?
В python существует два варианта проверки отрицательного условия:
if not x:
if x == False:
Какой из этих двух вариантов лучше на предмет удобочитаемости и/или быстродействия?
Ответиь на этот вопрос нам поможет PEP 285:
It has been suggested that, in order to satisfy user expectations, for every x that is considered true in a Boolean context, the expression x == True should be true, and likewise if x is considered false, x == False should be true. In particular newbies who have only just learned about Boolean variables are likely to write
if x == True:
...instead of the correct form,
if x:
...
Сокращенный перевод:
В частности, новички, которые только что узнали о логических переменных, скорее всего напишут if x == True:
вместо правильной формы if x:
Прежде всего, вариант
if x == False:
очень некрасивый — объект False
является одиночкой (singleton, единственным объектом), потому вместо оператора ==
(то же самое значение) рекомендуется применить оператор is
(тот же самый объект):
if x is False:
Из PEP 8 — руководства по написанию кода на Python:
Сравнения с None должны обязательно выполняться с использованием операторов is или is not, а не с помощью операторов сравнения.
Не совсем правильный перевод. В английском оригинале стоит немножко другое:
Сравнения с одиночками как None должны обязательно выполняться с использованием операторов is или is not, а не с помощью операторов сравнения.
Теперь, команды
if x is False:
if not x:
не всегда идентичны - в команде if
например пустой словарь вычисляется как False
:
x = {}
if x is False:
print("1. x is False") # не выполнится
if not x:
print("2. not x") # выполнится
Ну и теперь к вашим вопросам:
Удобочитаемость — сравните:
if (option in (1, 5, 6)) is False:
иif not option in (1, 5, 6):
Удобочитаемость зависит от того, кто это читает.;-) Для меня это второй вариант.
Быстродействие:
option = 4
%timeit if (option in (1, 5, 6)) is False: pass
%timeit if not option in (1, 5, 6): pass
161 ns ± 3.39 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each) 139 ns ± 1.88 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
Второй вариант чуть-чуть быстрее. Но не убедительно. По моему, интерпретатор Питона просто заранее не обнаружил совпадение, не оптимизировал первый вариант.
Прогнав такую проверку:
from time import perf_counter
from random import randint
def eq_compare(x):
return x == False
def not_compare(x):
return not x
lst = [[], {}, (), 1, 2, 3, True, None] + [randint(0, 10_000) for _ in range(1000)]
s = perf_counter()
for i in lst * 1000:
eq_compare(i)
print(perf_counter() - s)
s = perf_counter()
for i in lst * 100:
not_compare(i)
print(perf_counter() - s)
Скорость проверки через оператор not
выше, чем через оператор равенства в 10+ раз.
Вот несколько замеров (попарно):
== : 0.11482529999921098
not: 0.009857600030954927
== : 0.12146590003976598
not: 0.009981999988667667
== : 0.11241040000459179
not: 0.01083189999917522
Считаю, что медленность проверки через == обусловлена динамической типизацией - труднее динамически конвертировать типы в bool, так ещё потом и значения сравнивать.