Python использование нескольких операторов в логическом выражении - PullRequest
1 голос
/ 13 февраля 2020

Фон

Как мы все знаем, лучше не предполагать, что несколько переменных используют chain assignment, как a = b = [1,2,3], потому что a будет уменьшенной копией b. Это небезопасно, потому что a изменится, когда мы пересмотрим b.

Однако, если инициализация неизменна , мы можем сделать это a = b = 1, и это безопасно.

Недавно я обнаружил странное использование нескольких операторов в выражении условия потока управления , например if 1 < b < 2: или while a == b == c == 1:

Например, следующее управляющий поток выполняет разные фрагменты в разных условиях:

a = 1
b = 1
c = 2

if a == b == c == 1:
    print('All equal!')
else:
    print('At least one variable is not equal to others')

Хотя бы одна переменная не равна другим


Мой вопрос

Безопасно ли это использование нескольких операций в логическом выражении в потоке управления ? Я знаю, что мы должны проверить приоритет оператора , когда мы пишем логическое выражение . Есть ли что-то еще, за чем мы должны следить? Я пытаюсь некоторое время и думаю, что использование нескольких операторов безопасно.


Анализ байт-кода

Я набираю байт-код следующей программы:

a = 1;b =2;c =1.5
a<b<c
import dis
dis.dis('a<b<c')
  1           0 LOAD_NAME                0 (a)
              2 LOAD_NAME                1 (b)
              4 DUP_TOP
              6 ROT_THREE
              8 COMPARE_OP               0 (<)
             10 JUMP_IF_FALSE_OR_POP    18
             12 LOAD_NAME                2 (c)
             14 COMPARE_OP               0 (<)
             16 RETURN_VALUE
>   18 ROT_TWO
             20 POP_TOP
             22 RETURN_VALUE```

Я могу только признать, что он сравнивает a и b на шаге 10, а затем сравните a и c на шаге 14. Но почему он по-прежнему возвращает False. Я не знаком с анализом байт-кода . Если кто-то может помочь с анализом, я буду очень признателен! Вот официальное руководство по модулю : dis

Ответы [ 2 ]

1 голос
/ 13 февраля 2020

От 0 до 8 он сравнивает a < b, при 10 проверяет, является ли его False, если да go до 18, повернуть стек, значение pop top, то есть False , поскольку a<b<c равно a<b and b<c, поэтому, если первое значение равно False, не нужно проверять второе условие.

Но в этом случае a < b == True, поэтому оно продолжается. В этот момент, когда он прошел первую контрольную точку (10), он знает, что первое условие должно быть True, поэтому он возвращает любое значение из условия b < c, которое равно False, так что вы получите False.

Наоборот, если вы проверяете дизассемблирование 'a

  1           0 LOAD_NAME                0 (a)
              2 LOAD_NAME                1 (b)
              4 COMPARE_OP               0 (<)
              6 JUMP_IF_TRUE_OR_POP     14
              8 LOAD_NAME                1 (b)
             10 LOAD_NAME                2 (c)
             12 COMPARE_OP               0 (<)
        >>   14 RETURN_VALUE

, он выполняет обратное, проверяет (6), является ли первое условие True, если да, не имеет значения, что вычисляет следующее условие, и возвращает значение, иначе возвращает то, к чему относится следующее условие.

1 голос
/ 13 февраля 2020

Это код, данный вами.

a = 1
b = 1
c = 2

if a == b == c == 1:
    print('All equal!')
else:
    print('At least one variable is not equal to others')

Давайте разберемся, что это значит. a==b==c==1 оценивается как True только тогда, когда все три из них равны 1. Остальное оценивается до False. a==b==c оценивается как a==b and b==c and c==a.

Чтобы получить то, что вы хотели, вы должны сделать это.

if a==b==c==1:
    print('All are equal')
elif (a==b) or (b==c) or (c==a):
   print('At least one variable is not equal to others')
else:
    print('none of them are equal')

Теперь для анализа второго, который вы предоставили с помощью байт-кода.

a = 1;b =2;c =1.5
a<b<c

a<b<c оценивается как a<b and b<c, в вашем случае это 1<2 and 2<1.5, что оценивается как False. 1<2 оценивается в True, а 2<1.5 оценивается в False. True and False is evaluated to False`.

Байт-код:

In [2]: a=1;b=2;c=1.5

In [3]: dis.dis('a<b and b<c')
  1           0 LOAD_NAME                0 (a)
              2 LOAD_NAME                1 (b)
              4 COMPARE_OP               0 (<)
              6 JUMP_IF_FALSE_OR_POP    14
              8 LOAD_NAME                1 (b)
             10 LOAD_NAME                2 (c)
             12 COMPARE_OP               0 (<)
        >>   14 RETURN_VALUE

6 JUMP_IF_FALSE_OR_POP 14 Эта строка говорит нам о переходе на строку 14, если она ложна. В логический and False and any_bool_value всегда оценивается как False.

Теперь, если 6 JUMP_IF_FALSE_OR_POP 14 равно True, то он продолжает выполнять 8 для 14.

И безопасно использовать Несколько логических операторов в одном выражении .

...