Логический тип bool: true, false, логические операции и truthy/falsy

Логический тип bool: true, false, логические операции и truthy/falsy Полезное

Логический (булев) тип — это тип данных с двумя значениями: истина (true) и ложь (false). В Python он называется bool и пишется True/False, в Java — boolean, в JavaScript — boolean, в C — bool (с C99 через <stdbool.h>, с C23 — ключевое слово). Название дано в честь математика Джорджа Буля, который в середине XIX века описал алгебру логики.

Ниже разберем три вещи, которые на практике путают чаще всего: логический тип как таковой, логические операции над ним и «истинность» значений других типов (truthy/falsy), по которой работают if и while. Основные примеры — на Python, для сравнения — JavaScript, Java и C. Код проверен на Python 3.9+, Node.js 22 и компиляторе C с поддержкой C99.

Три понятия, которые нужно развести

Понятие Что это Пример
Логический тип Тип данных ровно с двумя значениями True, False в Python
Логическое выражение Выражение, результат которого проверяют как истину или ложь age >= 18, x in items
Истинность (truthiness) Как язык трактует НЕлогическое значение в условии if items: — пустой список ложен

Сравнения (==, <, >=), проверка вхождения (in) и функции-предикаты вроде str.isdigit() возвращают значение логического типа. А вот if в Python и JavaScript принимает значение любого типа и сам решает, считать ли его истиной. В Java так нельзя: условие обязано быть boolean.

Минимальный пример на Python

age = 20
has_ticket = False

is_adult = age >= 18
print(is_adult, type(is_adult))      # True <class 'bool'>

can_enter = is_adult and has_ticket
print(can_enter)                      # False

print(not has_ticket)                 # True
print(is_adult != has_ticket)         # True - это и есть XOR для bool

Вывод программы:

True <class 'bool'>
False
True
True

Разбор по строкам. age >= 18 — сравнение, его результат сохраняется в переменную типа bool. and дает истину, только если истинны оба операнда. not меняет значение на противоположное. Отдельного логического оператора XOR в Python нет: для двух значений типа bool его роль выполняет != (истина, когда значения разные). Побитовый ^ для bool тоже вернет bool, но для других типов это уже побитовая операция.

Логические операции и таблица истинности

Операция Python Java / JavaScript / C Истина, когда
И (конъюнкция) and && оба операнда истинны
ИЛИ (дизъюнкция) or \|\| хотя бы один истинен
НЕ (отрицание) not ! операнд ложен
Исключающее ИЛИ != для bool, ^ ^ в Java, !== для bool в JS операнды различаются

Таблица истинности для двух операндов:

A B A и B A или B A xor B
false false false false false
false true false true true
true false false true true
true true true true false

В булевой алгебре есть и другие операции, например штрих Шеффера (И-НЕ) и стрелка Пирса (ИЛИ-НЕ). В прикладном коде их отдельными операторами не пишут, они важны в схемотехнике: из одной такой операции можно собрать все остальные.

Приоритет

В Python приоритет такой: сначала сравнения, потом not, потом and, потом or. Поэтому a or b and c читается как a or (b and c). В Java порядок для логических операторов: !, затем &, ^, |, затем &&, затем ||. Практическое правило для любого языка: если в выражении смешаны И и ИЛИ, ставьте скобки — это дешевле, чем помнить таблицу приоритетов.

Короткое замыкание (short-circuit)

Короткое замыкание — вычисление второго операнда только тогда, когда без него результат неизвестен. Если левая часть and ложна, итог уже ложь, и правая часть не выполняется. Если левая часть or истинна, итог уже истина.

def check(name, value):
    print("вычисляю", name)
    return value

print(check("A", False) and check("B", True))
print("...")
print(check("A", True) or check("B", False))

Вывод:

вычисляю A
False
...
вычисляю A
True

Функция B не вызвалась ни разу. На этом держится частый защитный прием: сначала проверить, что объект есть, и только потом обращаться к его полям.

user = None
if user is not None and user.get("is_admin"):
    print("админ")
else:
    print("доступ закрыт")   # доступ закрыт

Если поменять операнды местами, user.get(...) выполнится первым и упадет с AttributeError, потому что у None нет метода get.

В Java и C# есть два варианта операторов: && и || с коротким замыканием, а & и | над boolean вычисляют оба операнда всегда. Одиночные &/| полезны редко — когда у правой части есть побочный эффект, который должен выполниться при любом результате. В обычных условиях используйте && и ||.

and и or в Python возвращают не bool

Это граница упрощения «логический оператор возвращает true или false». В Python and и or возвращают один из операндов, а не обязательно True/False. То же в JavaScript для && и ||.

print(0 or "по умолчанию")      # по умолчанию
print("Анна" or "по умолчанию") # Анна
print([] and 42)                # []
print(not [])                   # True

Отсюда идиома значения по умолчанию name = value or "гость". Но у нее есть ловушка: если 0 или пустая строка — допустимые значения, они тоже заменятся на значение по умолчанию. В таком случае проверяйте явно: value if value is not None else "гость". Оператор not всегда возвращает bool.

Truthy и falsy в разных языках

Когда в условие попадает не bool, язык приводит значение к истине или лжи по своим правилам. Значения, которые считаются ложью, называют falsy, остальные — truthy.

Язык Что ложно в условии Особенности
Python False, None, ноль любого числового типа, пустые строка, список, кортеж, словарь, множество, range(0) свои классы управляют этим через __bool__ или __len__
JavaScript false, 0, -0, 0n, "", null, undefined, NaN пустой массив [] и объект {} истинны
Java только false if (1) — ошибка компиляции, условие должно быть boolean
C ноль (включая нулевой указатель) любое ненулевое число — истина, результат сравнения — int 1 или 0

Проверка в JavaScript:

const values = [0, "", "0", [], {}, null, NaN, "false"];
for (const v of values) {
  console.log(JSON.stringify(v), Boolean(v));
}

Вывод:

0 false
"" false
"0" true
[] true
{} true
null false
null false
"false" true

Строка "0" и строка "false" истинны: непустая строка всегда truthy. Вторая строка null в выводе — это NaN, его так печатает JSON.stringify.

Типичная ошибка: строка «false» из конфига

Значения из переменных окружения, форм и CSV приходят строками. Прямое приведение к bool дает неверный результат.

Неверно:

raw = "false"            # например, из переменной окружения DEBUG
debug = bool(raw)
print(debug)             # True

Результат: True, потому что строка непустая. Исправление — явный разбор с белым списком значений и ошибкой на неизвестном вводе:

def parse_bool(raw):
    value = raw.strip().lower()
    if value in ("1", "true", "yes", "on"):
        return True
    if value in ("0", "false", "no", "off", ""):
        return False
    raise ValueError("не логическое значение: " + repr(raw))

print(parse_bool("false"))   # False
print(parse_bool(" TRUE "))  # True
try:
    parse_bool("maybe")
except ValueError as e:
    print(e)                 # не логическое значение: 'maybe'

Какие строки считать истиной — решение вашего проекта. Главное, чтобы неизвестное значение не превращалось молча в True или False.

bool и числа: где тип «подменяется» целым

В Python bool — подкласс int: True == 1, False == 0, и с ними работает арифметика. Это удобно для подсчета, но создает ловушки.

flags = [True, False, True, True]
print(sum(flags))              # 3 - число истинных значений
print(isinstance(True, int))   # True
print(True + True)             # 2
print(1 == True, type(1) is bool)  # True False

Из-за этого if x == True сработает и для x = 1. Сравнивать с True явно не нужно: пишите if x:, а если важно отличить именно True от других значений — if x is True:.

В C до стандарта C99 отдельного типа не было, истину обозначали ненулевым целым. Сейчас используют bool из <stdbool.h>:

#include <stdio.h>
#include <stdbool.h>

int main(void) {
    bool ready = 5;              /* любое ненулевое значение становится true */
    printf("%d\n", ready);       /* 1 */
    printf("%d\n", 3 > 2);       /* 1 - результат сравнения имеет тип int */
    printf("%zu\n", sizeof(bool));
    return 0;
}

Последняя строка обычно печатает 1, но размер bool в C стандартом не зафиксирован и зависит от платформы. Хотя для хранения логического значения достаточно одного бита, переменная занимает минимум адресуемую ячейку — обычно байт. Упаковка флагов в биты — отдельная техника (битовые поля, битовые маски), а не свойство типа.

Логический тип в SQL: третье значение

В SQL у логических выражений три значения: TRUE, FALSE и UNKNOWN, который возникает при сравнении с NULL. Пример в SQLite, где истина и ложь хранятся как 1 и 0:

SELECT NULL AND 0, NULL OR 1, NULL = NULL, 1 = 1;

Результат: [(0, 1, None, 1)]. NULL AND 0 — ложь, потому что ложного операнда достаточно. NULL = NULL — не истина, а NULL (UNKNOWN), поэтому для проверки пишут IS NULL. Хранение логического поля зависит от СУБД: в PostgreSQL есть тип BOOLEAN, в SQLite поле объявляют целым и ограничивают CHECK (flag IN (0, 1)).

Как выбрать проверку в условии

  1. Значение уже bool (результат сравнения, флаг) — пишите if flag: без == True.
  2. Нужно отличить «нет значения» от нуля или пустой строки — проверяйте is None явно, не полагайтесь на truthy.
  3. Проверка «коллекция не пуста» в Python — if items:, это идиоматично.
  4. Значение пришло извне строкой — разбирайте явно, как parse_bool выше.
  5. Второй операнд опасен без первого — ставьте защитную проверку левее, короткое замыкание не даст ему выполниться.

Выводы

  • Логический тип хранит два значения, true и false; сравнения и предикаты возвращают именно его.
  • Логические операции И, ИЛИ, НЕ и XOR описываются таблицей истинности; при смешении И и ИЛИ надежнее ставить скобки.
  • Короткое замыкание пропускает правый операнд, если результат уже известен, — на этом строятся защитные проверки.
  • Truthy/falsy различаются по языкам: [] ложен в Python и истинен в JavaScript, а Java принимает в условии только boolean.
  • Строку «false» нельзя превращать в логическое значение через bool() — нужен явный разбор.

Где применяется / связь с практикой

Освойте тему на практике

Логический тип встречается в каждом ветвлении, цикле, фильтре и проверке прав доступа, поэтому ошибки в нем трудно заметить: код не падает, а просто идет не по той ветке. Системно разобраться в типах, условиях и отладке таких мест на Python можно на курсе Python Developer. Basic. Посмотреть формат обучения и задать вопросы преподавателям можно на бесплатных открытых уроках.

FAQ

Почему в Python True пишется с большой буквы?
В Python 3 True и False — ключевые слова, а язык регистрозависим: true без объявления вызовет NameError.

Можно ли применять ~ к bool в Python?
Технически это побитовая инверсия целого: ~True дает -2, а не False. Начиная с Python 3.12 такое использование помечено как устаревшее, для отрицания нужен not.

Чем логический тип отличается от флага в битовой маске?
Переменная bool хранит одно значение и обычно занимает байт. Битовая маска упаковывает много флагов в одно целое, и каждый читается операцией & с нужным битом.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)