Как на самом деле работают цепные сравнения в Python?
Python Doc для сравнения говорит:
Сравнения могут быть связаны произвольно, например,
x < y <= zэквивалентноx < y and y <= z, за исключением того, чтоyоценивается только один раз (но в обоих случаяхzне оценивается вообще, когдаx < yоказывается ложным).
И эти так вопросы / ответы проливают еще немного света на такое использование:
- операторы сравнения Python цепочка / группировка слева направо?
- что делает "оценено только один раз" означает для цепных сравнений в Python?, в частности принятый в настоящее время ответ
Итак, что-то вроде (надуманный пример):
if 1 < input("Value:") < 10: print "Is greater than 1 and less than 10"
Запрашивает ввод только один раз. В этом есть смысл. И вот что:
if 1 < input("Val1:") < 10 < input("Val2:") < 20: print "woo!"
Только просит Val2 Если Val1 находится между 1 и 10 и только печатает "ууу!"Если Val2 также между 10 и 20 (доказывая, что они могут быть "скованы произвольно"). Это тоже имеет смысл.
Но я ... все еще любопытно, как это на самом деле реализуется/интерпретируется на уровне лексера/парсера/компилятора (или чего-то еще).
Первый пример выше в основном реализован следующим образом:
x = input("Value:")
1 < x and x < 10: print "Is between 1 and 10"
Где x реально существует (и фактически по существу не имеет названия) только для этих сравнений? Или это каким-то образом заставляет оператор сравнения возвращать и булев результат, и оценку правильного операнда (который будет использоваться для дальнейшего сравнения) или что-то в этом роде?
Расширение анализ второго примера приводит меня к мысли, что он использует что-то вроде безымянного промежуточного результата (кто-то просветит меня, если для этого есть термин), поскольку он не оценивает все операнды перед сравнением.
1 ответ:
Вы можете просто позволить Python сказать вам, какой байт-код создается с помощью
disмодуль :>>> import dis >>> def f(): return 1 < input("Value:") < 10 ... >>> dis.dis(f) 1 0 LOAD_CONST 1 (1) 3 LOAD_GLOBAL 0 (input) 6 LOAD_CONST 2 ('Value:') 9 CALL_FUNCTION 1 12 DUP_TOP 13 ROT_THREE 14 COMPARE_OP 0 (<) 17 JUMP_IF_FALSE_OR_POP 27 20 LOAD_CONST 3 (10) 23 COMPARE_OP 0 (<) 26 RETURN_VALUE >> 27 ROT_TWO 28 POP_TOP 29 RETURN_VALUEPython использует стек;
CALL_FUNCTIONбайт-код использует элементы в стеке (inputglobal и'Value:'string) для вызова функции с одним аргументом, чтобы заменить эти два элемента в стеке результатом вызова функции. Перед вызовом функции константа1загружалась в стек.Так что к тому времени
inputбыл назван стек выглядит например:input_result 1И
DUP_TOPдублирует верхнее значение, прежде чем вращает три верхних стека значений, чтобы получить:1 input_result input_resultИ a
COMPARE_OP, чтобы проверить два верхних элемента с помощью<, заменив два верхних элемента результатом.Если этот результат был
FalseJUMP_IF_FALSE_OR_POPбайт-код переходит к 27, который поворачиваетFalseсверху с оставшимсяinput_result, чтобы очистить его с помощьюPOP_TOP, чтобы затем вернуть оставшеесяFalseверхнее значение в качестве результат.Если результат
Trueоднако, это значение выскакивает из стека байт-кодомJUMP_IF_FALSE_OR_POPи на его место загружается значение10сверху, и мы получаем:10 input_resultИ вместо этого делается и возвращается другое сравнение.
Таким образом, по существу Python делает следующее:
stack_1 = stack_2 = input('Value:') if 1 < stack_1: result = False else: result = stack_2 < 10Со значениями
stack_*, очищенными снова.Стек, таким образом, содержит неназванный промежуточный результат для сравнения