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

Проверка различий в уровне 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

Есть ли какая-либо разница, какая-либо разница в терминах процесса оценки или исполнения или времени? а также какой из них лучше или лучше использовать?

Связано ли это с понятием транзитивности?

4b9b3361

Ответ 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.