Почему "и" и "или" возвращают операнды в Python?

Я переживаю LPTHW, и я наткнулся на то, что не могу понять. Когда вам когда-нибудь понадобится, чтобы ваш логический and или or возвращал нечто, отличное от логического? В тексте LPTHW указано, что все языки, такие как python, имеют такое поведение. Имеет ли он смысл интерпретировать или скомпилированные языки или утку, типизированную по сравнению с статическими типизированными языками?

Я запустил следующий код:

>>> False and 1
False
>>> True and 1
1
>>> 1 and False
False
>>> 1 and True
True
>>> True and 121
121
>>> False or 1
1
>>> False or 112
112
>>> False or "Khadijah"
'Khadijah'
>>> True and 'Khadijah'
'Khadijah'
>>> False or 'b'
'b'
>>> b = (1, 2, "K")
>>> b
(1, 2, 'K')
>>> False or b
(1, 2, 'K')
>>> 

Пожалуйста, помогите мне понять, что происходит здесь.

Согласно документации: http://docs.python.org/2/library/stdtypes.html

Операции и встроенные функции, которые имеют логический результат, всегда возвращают 0 или False для false и 1 или True для true, если не указано иное. (Важное исключение: логические операции or и and всегда возвращают один из их операндов.)

Согласно LPTHW: http://learnpythonthehardway.org/book/ex28.html Почему "test" and "test" возвращает "test" или 1 and 1 возвращает 1 вместо True? Python и многие другие языки возвращают один из операндов в их логические выражения, а не только True или False. Это означает, что если вы сделали False и 1, вы получите первый операнд (False), но если вы сделаете True и 1, получите второй (1). Играйте с этим немного.

Ответ 1

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

Операции и встроенные функции, которые имеют логический результат, всегда возвращают 0 или False для false и 1 или True для true, если не указано иное. (Важное исключение: логические операции или и и всегда возвращают один из их операндов.)

Как вы можете видеть, вы правы в отношении операций и встроенных функций, но смотрите раздел "Важные исключения", хорошо сказано, что логические операторы вернут один из своих операндов.

Теперь то, что они могут вернуть, вряд ли зависит от логики короткого замыкания оператора. Для оператора or он вернет первое истинное значение в выражении, так как, когда оно находит одно, все выражение истинно. В случае, если каждый операнд является ложным, or вернет последний операнд, что означает, что он повторяется, когда каждый из них не может найти правдивый.

Для оператора and, если выражение истинно, оно вернет последний операнд, если выражение ложно, оно вернет первый ложный операнд. Вы можете больше узнать о оценка короткого замыкания на странице Википедии.

У вас есть много примеров в вашем вопросе, пусть проанализируют некоторые из них:

>>> False and 1  # return false (short circuited at first falsey value)
False
>>> True and 1   # return 1 (never short circuited and return the last truthy value)
1
>>> 1 and False  # return false (short circuited at first falsey value, in this case the last operand)
False
>>> 1 and True  # return True (never short circuited and return the last truthy value)
True
>>> True and 121  # return 121 (never short circuited and return the last truthy value)
121
>>> False or 1  # return 1 (since the first operand was falsey, or kept looking for a first truthy value which happened to be the last operator)
1
>>> False or 112  # return 112 for same reason as above
112
>>> False or "Khadijah"  # return "Khadijah" for same reason as above
'Khadijah'
>>> True and 'Khadijah'  # return "Khadijah" because same reason as second example
'Khadijah'

Я думаю, что это должно сделать точку. Чтобы помочь вам понять, почему это полезно, рассмотрите следующий пример:

У вас есть функция, которая произвольно генерирует имена

import random

def generate_name():
    return random.choice(['John', 'Carl', 'Tiffany'])

и у вас есть переменная, которую вы не знаете, если она назначила имя, но вместо этого:

if var is None:
    var = generate_name()

Вы можете сделать oneliner:

var = var or generate_name()

Так как None является значением false, or продолжит поиск и оценит второй операнд, это вызовет функцию, в конечном счете возвращающую сгенерированное имя. Это очень глупый пример, я видел лучшие способы использования (хотя и не в Python) такого стиля. Сейчас я не смог выступить с лучшим примером. Вы также можете взглянуть на эти вопросы, есть очень полезные ответы на эту тему: Поддерживает ли Python короткое замыкание?

И последнее, но не менее важное: это не имеет ничего общего со статическим типизированным, утиным, динамическим, интерпретированным, скомпилированным, любым языком. Это просто функция языка, которая может пригодиться, и это очень распространено, поскольку почти каждый язык программирования, который я могу представить, предоставляет эту функцию.

Надеюсь, это поможет!

Ответ 2

Можно было бы хотеть and и or оценивать операнд (в отличие от того, чтобы всегда оценивать True или False), чтобы поддерживать идиомы, подобные следующим:

def foo(self):
    # currentfoo might be None, in which case return the default
    return self.currentfoo or self.defaultfoo()

def foobar(self):
    # foo() might return None, in which case return None
    foo = self.foo()
    return foo and foo.bar()

Конечно, вы можете задать вопрос о значении таких идиом, особенно если вы не привыкли к ним. Всегда можно написать эквивалентный код с явным if.

В качестве пункта против них они оставляют сомнение в том, что полный набор значений ложности возможен и преднамеренно учтен, или только тот, который указан в комментарии (с другими недопустимыми значениями). Но тогда это верно в общем случае кода, который использует истинность значения, которое может быть чем-то иным, чем True или False. Это иногда, но редко приводит к недоразумениям.

Ответ 3

Это связано с тем, как в Python реализован эффект короткого замыкания.

С and (помните True and X = X) результат правильного выражения помещается в стек, если он ложный, он сразу появляется, иначе появляется второе выражение:

>>> import dis
>>> 
>>> dis.dis(lambda : True and 0)
  1           0 LOAD_CONST               2 (True)
              3 JUMP_IF_FALSE_OR_POP     9
              6 LOAD_CONST               1 (0)
        >>    9 RETURN_VALUE
>>>
>>> True and 0
0
>>> 0 and True
0
>>>

smiler:

def exec_and(obj1, obj2):
    if bool(obj1) != False:
        return obj2
    return obj1

С or, если первое выражение истинно, оно сразу появляется. Если нет, второе выражение будет выведено, теперь результат действительно зависит от второго.

>>> dis.dis(lambda : 0 or False)
  1           0 LOAD_CONST               1 (0)
              3 JUMP_IF_TRUE_OR_POP      9
              6 LOAD_CONST               2 (False)
        >>    9 RETURN_VALUE
>>>
>>> True or 0
True
>>> 1 or False
1
>>> False or 1
1
>>>

smiler:

def exec_or(obj1, obj2):
    if bool(obj1) != True:
        return obj2
    return obj1

Ответ 4

Рассмотрим следующий прецедент:

element = dict.has_key('foo') and dict['foo']

Установит element в dict['foo'], если он существует, иначе False. Это полезно при записи функции для возврата значения или False при сбое.

Дополнительный вариант использования с or

print element or 'Not found!'

Объединение этих двух строк будет распечатываться dict['foo'], если оно существует, иначе оно будет печатать 'Not found!' (я использую str(), иначе or терпит неудачу, если element есть 0 (или False), потому что это считается ложным, и поскольку мы печатаем его, это не имеет значения)

Это можно упростить до

print dict.has_key('foo') and str(dict['foo']) or 'Not found!'

И функционально эквивалентен:

if dict.has_key('foo'):
    print dict['foo']
else:
    print 'Not found!'