Проверка различий в уровне Python
Предположим, что мы хотим, чтобы какой-то блок кода выполнялся, когда оба "a" и "b" равны 5. Тогда мы можем написать так:
if a == 5 and b == 5:
# do something
Но несколько дней назад я просто невольно написал аналогичную проверку состояния как:
if a == b and b == 5:
# do something
который заставлял меня думать, есть ли разница между ними?
Кроме того, есть еще один способ,
if a == b == 5:
# do something
Есть ли какая-либо разница, какая-либо разница в терминах процесса оценки или исполнения или времени? а также какой из них лучше или лучше использовать?
Связано ли это с понятием транзитивности?
Ответы
Ответ 1
Поскольку они в основном эквивалентны, вы также можете подумать о том, как вы читаете/думаете о коде:
if a == 5 and b == 5:
# do something
можно читать как "если a
равно 5
и b
равно 5
, то do...". Вы должны думать/заключать, что тогда a
будет равно b
.
Это противоположно следующему примеру:
if a == b and b == 5:
# do something
Это выглядит как "если a
равно b
и b
, равному 5
", и вы должны сделать вывод, что тогда a
будет равно 5
Вот почему я предпочитаю последний пример:
if a == b == 5:
# do something
Если вы знакомы с Python (благодаря Itzkata), сразу становится ясно, что все три вещи должны быть равны (до 5
). Если, однако, люди с меньшим опытом работы на Python (но навыки программирования на других языках) видят это, они могут оценить это на
if (a == b) == 5:
который сравнивал бы логический результат первого сравнения с целым числом 5, что не то, что делает Python, и может привести к разным результатам (например, с помощью a=0, b=0
: a==b==0
истинно, а (a==b) == 0
не является
руководство говорит:
В Python имеется восемь операций сравнения. Все они имеют тот же приоритет (который выше, чем у булевых операций). Сравнения могут быть скованы произвольно; например, x < y <= z является эквивалентна x < y и y <= z, , за исключением того, что y оценивается только один раз(но в обоих случаях z вообще не оценивается, когда x < y оказывается ложь).
Там может быть даже разница, например, если evaulating b
в вашем примере будет иметь побочный эффект.
Что касается транзитивности, вы правы.
Ответ 2
Если у вас есть больше переменных для тестирования, использование all
может быть несколько более читаемым:
if all(i==5 for i in [a,b,c,d]):
# do something
Ответ 3
Что касается целых чисел, то между первыми двумя сравнениями нет разницы в отношении максимальной производительности.
Третье сравнение отличается, однако; так как задействовано немного больше возиться со стеком. Действительно, код
import dis
def comparison_1(a, b):
if a == 5 and b == 5:
pass
def comparison_2(a, b):
if a == b and b == 5:
pass
def comparison_3(a, b):
if a == b == 5:
pass
print("*** First comparison ***")
dis.dis(comparison_1)
print("\n*** Second comparison ***")
dis.dis(comparison_2)
print("\n*** Third comparison ***")
dis.dis(comparison_3)
возвращает
*** First comparison ***
4 0 LOAD_FAST 0 (a)
3 LOAD_CONST 1 (5)
6 COMPARE_OP 2 (==)
9 POP_JUMP_IF_FALSE 27
12 LOAD_FAST 1 (b)
15 LOAD_CONST 1 (5)
18 COMPARE_OP 2 (==)
21 POP_JUMP_IF_FALSE 27
5 24 JUMP_FORWARD 0 (to 27)
>> 27 LOAD_CONST 0 (None)
30 RETURN_VALUE
*** Second comparison ***
8 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 COMPARE_OP 2 (==)
9 POP_JUMP_IF_FALSE 27
12 LOAD_FAST 1 (b)
15 LOAD_CONST 1 (5)
18 COMPARE_OP 2 (==)
21 POP_JUMP_IF_FALSE 27
9 24 JUMP_FORWARD 0 (to 27)
>> 27 LOAD_CONST 0 (None)
30 RETURN_VALUE
*** Third comparison ***
12 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 DUP_TOP
7 ROT_THREE
8 COMPARE_OP 2 (==)
11 JUMP_IF_FALSE_OR_POP 23
14 LOAD_CONST 1 (5)
17 COMPARE_OP 2 (==)
20 JUMP_FORWARD 2 (to 25)
>> 23 ROT_TWO
24 POP_TOP
>> 25 POP_JUMP_IF_FALSE 31
13 28 JUMP_FORWARD 0 (to 31)
>> 31 LOAD_CONST 0 (None)
34 RETURN_VALUE
Ответ 4
Это зависит. Вы можете написать свой собственный __eq__
, который позволяет сравнивать себя с целями и вещами:
class NonNegativeInt(object):
def __init__(self, value):
if value < 0:
raise Exception("Hey, what the...")
self.value = value
def __eq__(self, that):
if isinstance(that, int):
return self.value == that
elif isinstance(that, NonNegativeInt):
return self.value == that.value
else:
raise ArgumentError("Not an acceptible argument", "__eq__", that)
который будет работать по-разному в зависимости от сравнения "b" и "a" и "b" с "int". Следовательно, a == b
может быть ложным, а a == 5 and b == 5
может быть True.