Использование True, False и None в качестве возвращаемых значений в функциях python

Я думаю, что я полностью понимаю это, но я просто хочу убедиться, так как я продолжаю видеть, как люди говорят никогда не тестировать против True, False или None. Они предполагают, что подпрограммы должны вызывать ошибку, а не возвращать False или None. Во всяком случае, у меня есть много ситуаций, когда я просто хочу знать, установлен ли флаг или нет, поэтому моя функция возвращает True или False. Есть и другие ситуации, когда у меня есть функция return None, если не было полезного результата. Из моего мышления, ни проблематично, пока я понимаю, что я никогда не должен использовать:

if foo == True
if foo == False
if foo == None

и вместо него следует использовать:

if foo is True
if foo is False
if foo is None

поскольку True, False и None - все синглеты и всегда будут оценивать так, как я ожидаю при использовании "is", а не "==". Я ошибаюсь?

наряду с этим, было бы более подходящие для Python, чтобы изменить функции, иногда нет, так что они вместо того, чтобы вызвать ошибку? Скажем, у меня есть метод экземпляра под названием "get_attr ()", который извлекает атрибут из какого-то файла. В случае, когда он обнаруживает, что запрошенный атрибут не существует, целесообразно ли возвращать None? Было бы лучше, если бы они подняли ошибку и поймали ее позже?

9 ответов


совет не в том, что вы никогда не должны использовать True, False или None. Это просто, что вы не должны использовать if x == True.

if x == True - это глупо, потому что == это просто двоичный оператор! Он имеет возвращаемое значение либо True или False, в зависимости от того, равны ли его аргументы или нет. И if condition будет продолжена, если condition - это правда. Поэтому, когда вы пишете if x == True Python собирается сначала оценить x == True, который станет True Если x был True и False в противном случае, а затем, если результат это верно. Но если вы ожидаете x либо True или False, Почему бы просто не использовать if x напрямую!

кроме того, x == False обычно можно заменить на not x.

есть некоторые обстоятельства, где вы можете использовать x == True. Это потому что if условие оператора "оценивается в булевом контексте", чтобы увидеть, является ли оно" истинным", а не тестируется точно против True. Например, непустые строки, списки и словари считаются истинными оператором if, а также ненулевые числовые значения, но ни одно из них не равно True. Поэтому, если вы хотите проверить, является ли произвольное значение ровно значение True не только ли это истина, когда вы будете использовать if x == True. Но я почти никогда не вижу в этом смысла. Это так редко, что если вы do когда-либо нужно написать это, стоит добавить комментарий Так будущее разработчики (включая, возможно, вас) не просто предполагают == True лишнее и удалить его.


используя x is True вместо этого на самом деле хуже. Вы никогда не должны использовать is базовый встроенный неизменяемые типы как логические (True, False), числа и строки. Причина в том, что для этих типов мы заботимся о значения, а не личность. == тесты, что значения одинаковы для этих типов, в то время как is всегда тесты Identities.

тестирование идентификаторов, а не значений плохо, потому что реализация теоретически может создавать новые логические значения, а не находить существующие, что приводит к тому, что у вас есть два True значения, которые имеют одно и то же значение, но хранятся в разных местах в памяти и имеют разные идентификаторы. На практике я почти уверен True и False всегда повторно используются интерпретатором Python, поэтому этого не произойдет, но это действительно деталь реализации. Этот проблема заставляет людей все время работать со строками, потому что короткие строки и литеральные строки, которые появляются непосредственно в источнике программы, перерабатываются Python so 'foo' is 'foo' всегда возвращает True. Но легко построить одну и ту же строку 2 разными способами и заставить Python дать им разные идентификаторы. Наблюдаем следующее:

>>> stars1 = ''.join('*' for _ in xrange(100))
>>> stars2 = '*' * 100
>>> stars1 is stars2
False
>>> stars1 == stars2
True

EDIT: Итак, оказывается, что равенство Python на булевых немного неожиданное (по крайней мере, для me):

>>> True is 1
False
>>> True == 1
True
>>> True == 2
False
>>> False is 0
False
>>> False == 0
True
>>> False == 0.0
True

обоснование этого, как объясняется в заметки, когда bools были введены в Python 2.3.5, что старое поведение использования целых чисел 1 и 0 для представления True и False было хорошим, но мы просто хотели больше описательных имен для чисел, которые мы намеревались представить значения истины.

один из способов добиться этого - просто иметь True = 1 и False = 0 в builtins; тогда 1 и True действительно были бы неразличимы (включая is). Но это также означает, что функция returning True покажет 1 в интерактивном интерпретаторе, так что вместо этого было сделано, чтобы создать bool как подтип int. Единственное, что отличается от bool is str и repr; bool экземпляры по-прежнему имеют те же данные, что и int экземпляры, и все равно сравнить равенство таким же образом, так что True == 1.

поэтому неправильно использовать x is True, когда x возможно, был установлен по некоторому коду, который ожидает, что "True - это просто еще один способ написания 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. Но это становится довольно многословным для довольно неясного дела. Он также не очень подходящие для Python, делая явной проверки типа... но это действительно то, что вы делаете, когда пытаетесь проверить точно True вместо truthy; способ ввода утки будет принимать истинные значения и позволять любому определяемому пользователем классу объявлять себя истинным.

если вы имея дело с этим чрезвычайно точным понятием истины, где вы не только не считаете непустые коллекции истинными, но и не считаете 1 истинным, а затем просто используете x is True вероятно, все в порядке, потому что предположительно тогда вы знаете, что x не из кода, который считает 1 истинным. Я не думаю, что есть какой-либо способ чистого питона придумать другой True который живет на другом адресе памяти (хотя вы, вероятно, могли бы сделать это из C), поэтому это никогда не должно сломаться, несмотря на быть теоретически "неправильным" поступком.

и я привык думать, что булевы были простыми!

End Edit


в случае None, однако, идиома заключается в использовании if x is None. Во многих случаях вы можете использовать if not x, потому что None является значением "falsey" для 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.


использовать if foo или if not foo, нет необходимости в либо == или is для этого.

для проверки против None,is None и есть. Это позволяет отличить его от False (или вещей, которые оцениваются как False, например "" и []).

ли get_attr должен возвратить None будет зависеть от контекста. У вас может быть атрибут, где значение равно None, и вы не сможете этого сделать. Я бы истолковал None as значение "отключено", и KeyError будет означать, что ключ не существует в файле.


если проверка на правдивость:

if foo

для false:

if not foo

нет:

if foo is None

для non-none:

if foo is not None

на getattr() правильное поведение-это не вернуться None но поднять AttributError ошибка вместо этого-если ваш класс не что-то вроде defaultdict


относительно того, следует ли создавать исключение или возвращать None: Это зависит от варианта использования. Или может быть обновления. Посмотрите на python dict например класс x[y] до dict.__getitem__ и он поднимает KeyError если ключ отсутствует. Но!--8--> метод возвращает второй аргумент (который по умолчанию None), если ключа нет. они оба полезны.

самая важная вещь, котор нужно рассматривать документировать это поведение в docstring, и убедитесь, что ваш get_attr() метод делает то, что он говорит, он делает.

чтобы ответить на другие ваши вопросы, используйте следующие соглашения:

if foo:
    # for testing truthiness

if not foo:
    # for testing falsiness

if foo is None:
    # testing .. Noneliness ?

if foo is not None:
    # check explicitly avoids common bugs caused by empty sequences being false

функции, которые возвращают True или False вероятно, должно иметь имя, которое делает это очевидным для улучшения читаемости кода

def is_running_on_windows():
    return os.name == 'nt'

в python3 вы можете "ввести подсказку", что:

>>> def is_running_on_windows() -> bool:
...     return os.name == 'nt'
... 
>>> is_running_on_windows.__annotations__
{'return': bool}

Вы можете непосредственно проверить, что переменная содержит значение или не нравится if var или not var.


в примерах PEP 8(руководство по стилю для кода Python) документ, Я видел, что foo is None или foo is not None используются вместо foo == None или foo != None. Также используя if boolean_value рекомендуется в этом документе вместо if boolean_value == True или if boolean_value is True. Поэтому я думаю, что если это официальный путь Python, мы, ребята Python, тоже должны идти этим путем.

С уважением.


в случае вашего вымышленного getattr function, если запрошенный атрибут всегда должен быть доступен, но затем не выдает ошибку. Если атрибут является необязательным, то return None.


для True, а не None:

if foo:

для false нет:

if not foo:

одна вещь, чтобы гарантировать, что ничто не может переназначить вашу переменную. Если он не является логическим в конце концов, полагаясь на правдивость приведет к ошибкам. Красота условного программирования на динамически типизированных языках :).

следующие отпечатки "нет".

x = False
if x:
    print 'yes'
else:
    print 'no'

теперь давайте изменим x.

x = 'False'

теперь заявление принтами "да " потому что строка правдива.

if x:
    print 'yes'
else:
    print 'no'

такое заявление, однако правильно выводит"нет".

if x == True:
    print 'yes'
else:
    print 'no'