Поддерживает ли Python короткое замыкание?

Ответы:

192

Замыкание поведения оператора and, or:

Давайте сначала определим полезную функцию, чтобы определить, выполняется что-то или нет. Простая функция, которая принимает аргумент, печатает сообщение и возвращает ввод без изменений.

>>> def fun(i):
...     print "executed"
...     return i
... 

Можно наблюдать поведение короткого замыкания Питона из and, orоператоров в следующем примере:

>>> fun(1)
executed
1
>>> 1 or fun(1)    # due to short-circuiting  "executed" not printed
1
>>> 1 and fun(1)   # fun(1) called and "executed" printed 
executed
1
>>> 0 and fun(1)   # due to short-circuiting  "executed" not printed 
0

Примечание . Следующие значения рассматриваются интерпретатором как ложные:

        False    None    0    ""    ()    []     {}

Поведение при коротком замыкании в функции: any(), all():

Python any()и all()функции также поддерживают короткое замыкание. Как показано в документах; они оценивают каждый элемент последовательности по порядку, пока не найдут результат, позволяющий досрочно выйти из оценки. Рассмотрим примеры ниже, чтобы понять оба.

Функция any()проверяет, является ли какой-либо элемент Истиной. Он прекращает выполнение, как только встречается True, и возвращает True.

>>> any(fun(i) for i in [1, 2, 3, 4])   # bool(1) = True
executed
True
>>> any(fun(i) for i in [0, 2, 3, 4])   
executed                               # bool(0) = False
executed                               # bool(2) = True
True
>>> any(fun(i) for i in [0, 0, 3, 4])
executed
executed
executed
True

Функция all()проверяет, что все элементы имеют значение True, и прекращает выполнение, как только встречается False:

>>> all(fun(i) for i in [0, 0, 3, 4])
executed
False
>>> all(fun(i) for i in [1, 0, 3, 4])
executed
executed
False

Поведение при коротком замыкании в цепочечном сравнении:

Дополнительно в Python

Сравнения могут быть связаны произвольно ; например, x < y <= zэквивалентно x < y and y <= z, за исключением того, что yоценивается только один раз (но в обоих случаях zвообще не оценивается, когда x < yобнаруживается ложное значение).

>>> 5 > 6 > fun(3)    # same as:  5 > 6 and 6 > fun(3)
False                 # 5 > 6 is False so fun() not called and "executed" NOT printed
>>> 5 < 6 > fun(3)    # 5 < 6 is True 
executed              # fun(3) called and "executed" printed
True
>>> 4 <= 6 > fun(7)   # 4 <= 6 is True  
executed              # fun(3) called and "executed" printed
False
>>> 5 < fun(6) < 3    # only prints "executed" once
executed
False
>>> 5 < fun(6) and fun(6) < 3 # prints "executed" twice, because the second part executes it again
executed
executed
False

Edit:
Еще один интересный момент , чтобы отметить : - Logical and,or операторы в Python возвращает операнд значения вместо Boolean ( Trueили False). Например:

Операция x and yдает результатif x is false, then x, else y

В отличие от других языков, например &&, ||операторы в C, которые возвращают 0 или 1.

Примеры:

>>> 3 and 5    # Second operand evaluated and returned 
5                   
>>> 3  and ()
()
>>> () and 5   # Second operand NOT evaluated as first operand () is  false
()             # so first operand returned 

Аналогично, orоператор возвращает самое левое значение, для которого bool(value)== Trueостальное самое правое ложное значение (в соответствии с коротким замыканием), примеры

>>> 2 or 5    # left most operand bool(2) == True
2    
>>> 0 or 5    # bool(0) == False and bool(5) == True
5
>>> 0 or ()
()

Итак, как это полезно? Один пример использования, приведенный в Practical Python Магнусом Ли Хетландом:
допустим, пользователь должен ввести свое имя, но может не вводить ничего, в этом случае вы хотите использовать значение по умолчанию '<unknown>'. Вы можете использовать оператор if, но вы также можете сформулировать очень кратко:

In [171]: name = raw_input('Enter Name: ') or '<Unkown>'
Enter Name: 

In [172]: name
Out[172]: '<Unkown>'

Другими словами, если возвращаемое значение из raw_input равно true (не пустая строка), оно присваивается name (ничего не меняется); в противном случае по умолчанию '<unknown>'назначается name.

Грижеш Чаухан
источник
1
Незначительный спор: явный список ложных значений немного вводит в заблуждение. Любой тип может иметь одно или несколько ложных значений. По соглашению, все числовые типы со значением 0являются falsy (так что это не просто 0, это 0.0, 0j, decimal.Decimal(0), fractions.Fraction(0)и т.д.), как и все коллекции с длиной 0(так в верхней части того, что вы перечислили, b''[PY3], u''[py2] и set()/ frozenset()являются все встроенные модули, которые оцениваются как ложные), но определяемые пользователем / сторонние типы могут определять свои собственные (с помощью __bool__[Py3] / __nonzero__[Py2] прямо или косвенно путем определения __len__).
ShadowRanger
@ShadowRanger здесь ваш комментарий завершит мой ответ. спасибо за добавление этой заметки.
Грижеш Чаухан
Кроме того, python выполняет двойную оценку короткозамкнутых условных выражений, если впоследствии они используются как логические значения ... если только они не находятся в операторе if, который является привилегированным
Эрик Аронест,
48

Да. Попробуйте следующее в вашем интерпретаторе Python:

и

>>>False and 3/0
False
>>>True and 3/0
ZeroDivisionError: integer division or modulo by zero

или

>>>True or 3/0
True
>>>False or 3/0
ZeroDivisionError: integer division or modulo by zero
Caprooja
источник