Яка різниця між цими двома рядками коду:
if not x == 'val':
і
if x != 'val':
Чи одна ефективніша за іншу?
Було б краще використовувати
if x == 'val':
pass
else:
Яка різниця між цими двома рядками коду:
if not x == 'val':
і
if x != 'val':
Чи одна ефективніша за іншу?
Було б краще використовувати
if x == 'val':
pass
else:
Відповіді:
Використовується dis
для перегляду байт-коду, згенерованого для двох версій:
not ==
4 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 2 (==)
9 UNARY_NOT
10 RETURN_VALUE
!=
4 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 3 (!=)
9 RETURN_VALUE
Останній має менше операцій, і тому, ймовірно, буде дещо ефективнішим.
Було відзначено, в commments (спасибі, @Quincunx ) , що якщо у вас є if foo != bar
проти if not foo == bar
кількість операцій точно так же, це тільки те , що COMPARE_OP
зміни і POP_JUMP_IF_TRUE
перемикається в POP_JUMP_IF_FALSE
:
not ==
:
2 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 2 (==)
9 POP_JUMP_IF_TRUE 16
!=
2 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 3 (!=)
9 POP_JUMP_IF_FALSE 16
У цьому випадку, якщо б не було різниці в обсязі роботи, необхідної для кожного порівняння, навряд чи ви побачите різницю в продуктивності.
Однак зауважте, що дві версії не завжди будуть логічно однаковими , оскільки це залежатиме від реалізації __eq__
та __ne__
для об'єктів, про які йдеться. Відповідно до документації щодо моделі даних :
Між операторами порівняння не маються на увазі зв’язки. Істина
x==y
не означає, щоx!=y
це помилково.
Наприклад:
>>> class Dummy(object):
def __eq__(self, other):
return True
def __ne__(self, other):
return True
>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True
Нарешті, і, мабуть, найголовніше: загалом там, де вони логічно однакові, x != y
набагато читабельніше, ніжnot x == y
.
__eq__
відповідає __ne__
, розбивається на плоскість.
not x == y
є ще одна інструкція. Коли я вклав код у if
, виявилося, що вони обоє мають однакову кількість інструкцій, просто одна мала, POP_JUMP_IF_TRUE
а друга POP_JUMP_IF_FALSE
(це була єдина різниця між ними, окрім використання іншого COMPARE_OP
). Коли я склав код без if
s, я отримав те, що у вас є.
==
і !=
не є взаємовиключними, - це SQL-подібна реалізація, що включає null
значення. У SQL null
не повертається true
до !=
порівняно з будь-яким іншим значенням, тому реалізація python інтерфейсів SQL також може мати ту саму проблему.
not ==
і !=
, здається, це найцікавіша частина моєї відповіді! Я не думаю, що тут не варто зупинятися, якщо, чому і коли це має сенс - див. Наприклад, чому Python має __ne__
метод оператора, а не просто __eq__
?
@jonrsharpe чудово пояснює, що відбувається. Я думав, що я просто покажу різницю у часі, коли запускати кожен із трьох варіантів 1000000000 разів (достатньо, щоб показати невелику різницю).
Використовуваний код:
def a(x):
if x != 'val':
pass
def b(x):
if not x == 'val':
pass
def c(x):
if x == 'val':
pass
else:
pass
x = 1
for i in range(10000000):
a(x)
b(x)
c(x)
І результати профайлера cProfile:
Таким чином, ми можемо бачити, що між if not x == 'val':
і між ними є різниця у хвилинах ~ 0.7% if x != 'val':
. З них if x != 'val':
найшвидший.
Однак, що найдивніше, ми можемо це побачити
if x == 'val':
pass
else:
насправді найшвидший і б'є if x != 'val':
на ~ 0,3%. Це не дуже читабельно, але, мабуть, якби ви хотіли незначного поліпшення продуктивності, можна було б піти по цьому маршруту.
У першому Python повинен виконати ще одну операцію, ніж потрібно (замість того, щоб просто перевірити, що не дорівнює, він повинен перевірити, чи не правда, що він рівний, таким чином, ще одну операцію). Неможливо було б визначити різницю від одного виконання, але якщо запустити багато разів, друге було б більш ефективним. Я б взагалі використовував другий, але математично вони однакові
>>> from dis import dis
>>> dis(compile('not 10 == 20', '', 'exec'))
1 0 LOAD_CONST 0 (10)
3 LOAD_CONST 1 (20)
6 COMPARE_OP 2 (==)
9 UNARY_NOT
10 POP_TOP
11 LOAD_CONST 2 (None)
14 RETURN_VALUE
>>> dis(compile('10 != 20', '', 'exec'))
1 0 LOAD_CONST 0 (10)
3 LOAD_CONST 1 (20)
6 COMPARE_OP 3 (!=)
9 POP_TOP
10 LOAD_CONST 2 (None)
13 RETURN_VALUE
Тут ви можете побачити, що це not x == y
ще одна інструкція, ніж x != y
. Тому різниця в продуктивності в більшості випадків буде дуже невеликою, якщо ви не робите мільйони порівнянь, і навіть тоді це, швидше за все, не стане причиною вузького місця.
Додаткове зауваження, оскільки інші відповіді відповідали на ваше питання здебільшого правильно, полягає в тому, що якщо клас визначає лише, __eq__()
а не __ne__()
, то ваш COMPARE_OP (!=)
запустить __eq__()
і заперечить його. У той час ваш третій варіант, ймовірно, буде крихітним, більш ефективним, але його слід враховувати лише в тому випадку, якщо вам ПОТРІБНА швидкість, оскільки це важко зрозуміти швидко.
Йдеться про ваш спосіб його читання. not
Оператор динамічний, тому ви можете його застосувати
if not x == 'val':
Але !=
можна сприймати в кращому контексті як оператор, який робить протилежне тому, що ==
робить.
not
оператор динамічний" ?
Я хочу розкрити свій коментар з читабельністю вище.
Знову ж таки, я цілком погоджуюся з читабельністю, що переважає інші (неістотні) ефекти.
Я хотів би зазначити, що мозок інтерпретує "позитив" швидше, ніж "негативно". Наприклад, "стоп" проти "не йди" (досить хитрий приклад через різницю в кількості слів).
Тож надано вибір:
if a == b
(do this)
else
(do that)
є кращим для функціонально-еквівалентного:
if a != b
(do that)
else
(do this)
Менша читабельність / зрозумілість призводить до збільшення помилок. Можливо, не в початковому кодуванні, але (не настільки розумний, як ви!) Зміна обслуговування ...