Ответ 1
Совет не в том, чтобы вы никогда не использовали True
, False
или None
. Просто вы не должны использовать if x == True
.
if x == True
глупо, потому что ==
просто бинарный оператор! Он имеет возвращаемое значение True
или False
, в зависимости от того, равны ли его аргументы или нет. И if condition
продолжится, если condition
истинно. Поэтому, когда вы пишете if x == True
, Python сначала оценит x == True
, который станет True
, если x
было True
, и False
в противном случае, и затем продолжит работу, если результат этого равен true. Но если вы ожидаете, что x
будет либо True
, либо False
, почему бы просто не использовать if x
напрямую!
Аналогично, x == False
обычно можно заменить на not x
.
В некоторых случаях вы можете использовать x == True
. Это связано с тем, что условие оператора if
"оценивается в булевом контексте", чтобы увидеть, является ли оно "правдивым", а не проверять точно по True
. Например, непустые строки, списки и словари считаются правильными с помощью оператора if, а также ненулевые числовые значения, но ни одно из них не равно True
. Поэтому, если вы хотите проверить, является ли произвольное значение точно значением True
, а не только является ли оно достоверным, когда вы используете if x == True
. Но я почти никогда не вижу в этом смысла. Это настолько редко, что если вам когда-нибудь понадобится написать это, стоит добавить комментарий, чтобы будущие разработчики (включая, возможно, и вы сами) не просто решили, что == True
излишен, и удалите его.
Использование x is True
вместо этого на самом деле хуже. Никогда не следует использовать is
с базовыми встроенными неизменяемыми типами, такими как логические значения (True
, False
), числа и строки. Причина в том, что для этих типов мы заботимся о значениях, а не о идентичности. ==
проверяет, что значения одинаковы для этих типов, а is
всегда проверяет идентичность.
Тестировать тождества, а не значения, плохо, потому что реализация теоретически может создавать новые логические значения, а не искать уже существующие, что приводит к тому, что у вас есть два значения True
, которые имеют одинаковое значение, но они хранятся в разных местах в памяти и имеют разные идентичностей. На практике я почти уверен, что True
и False
всегда повторно используются интерпретатором Python, так что этого не произойдет, но на самом деле это деталь реализации. Эта проблема все время сбивает людей с толку строками, потому что короткие строки и литеральные строки, которые появляются непосредственно в исходном тексте программы, перерабатываются Python, поэтому 'foo' is 'foo'
всегда возвращает True
. Но легко построить одну и ту же строку 2 разными способами и заставить Python присваивать им разные идентификаторы. Обратите внимание на следующее:
>>> stars1 = ''.join('*' for _ in xrange(100))
>>> stars2 = '*' * 100
>>> stars1 is stars2
False
>>> stars1 == stars2
True
ОБНОВЛЕНИЕ: Получается, что равенство Python в логических значениях немного неожиданно (по крайней мере для меня):
>>> True is 1
False
>>> True == 1
True
>>> True == 2
False
>>> False is 0
False
>>> False == 0
True
>>> False == 0.0
True
Обоснование этого, как объяснено в примечаниях, когда в Python 2.3.5 были введены bools, заключается в том, что старое поведение использования целых чисел 1 и 0 для представления True и False было хорошим, но мы нам просто нужно больше описательных имен для чисел, которые мы собирались представлять для истинных значений.
Одним из способов достижения этого было бы просто иметь True = 1
и False = 0
во встроенных программах; тогда 1 и True действительно будут неразличимы (в том числе is
). Но это также означает, что функция, возвращающая True
, будет показывать 1
в интерактивном интерпретаторе, поэтому вместо этого мы создали bool
как подтип int
. Единственное, что отличается от bool
- это str
и repr
; Экземпляры bool
по-прежнему имеют те же данные, что и экземпляры int
, и сравнивают равенство таким же образом, поэтому True == 1
.
Поэтому неправильно использовать x is True
, когда x
мог быть установлен каким-то кодом, который ожидает, что "Истина - это просто еще один способ написания 1", потому что есть много способов создать значения, которые равны True
, но не иметь такой же личности, как это:
>>> a = 1L
>>> b = 1L
>>> c = 1
>>> d = 1.0
>>> a == True, b == True, c == True, d == True
(True, True, True, True)
>>> a is b, a is c, a is d, c is d
(False, False, False, False)
И неправильно использовать x == True
, когда x
может быть произвольным значением Python, и вам нужно только знать, является ли оно логическим значением True
. Единственное, что у нас есть, это то, что использование x
лучше всего, когда вы просто хотите проверить "правдивость". К счастью, это обычно все, что требуется, по крайней мере, в коде, который я пишу!
Более верный путь был бы x == True and type(x) is bool
. Но это становится довольно многословным для довольно неясного случая. Это также выглядит не очень Pythonic, если выполнять явную проверку типов... но это действительно то, что вы делаете, когда пытаетесь проверить точно True
, а не правдиво; способ типизации утки должен был бы принять истинные значения и позволить любому пользовательскому классу объявить себя правдивым.
Если вы имеете дело с этим чрезвычайно точным понятием истины, когда вы не только не рассматриваете непустые коллекции как истинные, но и не рассматриваете 1 как истинные, тогда, вероятно, хорошо использовать только x is True
, потому что предположительно тогда вы знаете, что x
не пришел из кода, который считает 1 истинным. Я не думаю, что существует какой-либо чистый Python способ придумать другой True
, который живет по другому адресу памяти (хотя вы, вероятно, могли бы сделать это из C), так что это никогда не должно нарушаться, несмотря на то, что теоретически "неправильный" "что нужно сделать.
И раньше я думал, что логические выражения просты!
Конец редактирования
В случае None
, однако, идиома должна использовать if x is None
. Во многих случаях вы можете использовать if not x
, потому что None
является "ложным" значением для оператора if
. Но лучше всего делать это только в том случае, если вы хотите обрабатывать все значения Falsey (числовые типы с нулевым значением, пустые коллекции и None
) одинаково. Если вы имеете дело со значением, которое является каким-то другим возможным значением или None
, чтобы указать "нет значения" (например, когда функция возвращает None
в случае сбоя), то это намного лучше использовать if x is None
, чтобы вы случайно не предположили, что функция завершилась с ошибкой, когда она только что возвратила пустой список или число 0.
Мои аргументы в пользу использования ==
вместо is
для типов неизменяемых значений предполагают, что вы должны использовать if x == None
вместо if x is None
. Однако в случае None
Python явно гарантирует, что во всей вселенной есть ровно один None
, а обычный идиоматический код Python использует is
.
Относительно того, возвращать None
или вызывать исключение, это зависит от контекста.
Для чего-то вроде вашего примера get_attr
я бы ожидал, что оно вызовет исключение, потому что я буду называть его как do_something_with(get_attr(file))
. Обычное ожидание вызывающих абонентов состоит в том, что они получат значение атрибута, а получение им None
и предположения, что это значение атрибута, представляет собой гораздо худшую опасность, чем забывание обрабатывать исключение, когда вы действительно можете продолжить, если атрибут может не может быть найдено. Кроме того, возврат None
для обозначения сбоя означает, что None
не является допустимым значением для атрибута. Это может быть проблемой в некоторых случаях.
Для воображаемой функции, такой как see_if_matching_file_exists
, для которой мы предоставляем шаблон, и она проверяет несколько мест, чтобы увидеть, есть ли совпадение, она может вернуть совпадение, если найдет одну, или None
, если не найдет. Но в качестве альтернативы он может вернуть список совпадений; тогда ни одно совпадение не является просто пустым списком (который также является "ложным"; это одна из тех ситуаций, в которых я просто использовал бы if x
, чтобы увидеть, получил ли я что-нибудь обратно).
Поэтому, выбирая между исключениями и None
для указания сбоя, вы должны решить, является ли None
ожидаемым значением отказа, а затем посмотреть на ожидания кода, вызывающего функцию. Если "нормальное" ожидание состоит в том, что будет возвращено действительное значение, и только изредка вызывающая сторона сможет нормально работать независимо от того, возвращено ли действительное значение или нет, тогда вы должны использовать исключения для указания сбоя. Если это будет довольно распространенным явлением, когда не будет действительного значения, поэтому вызывающие абоненты будут ожидать обработки обеих возможностей, тогда вы можете использовать None
.