Python: Логические операторы
Мы уже умеем писать функции, которые проверяют одиночные условия. А в этом уроке научимся строить составные условия.
Предположим, что сайт при регистрации требует, чтобы пароль был длиннее восьми символов и содержал хотя бы одну заглавную букву. Попробуем написать два отдельных логических выражения и соединим их специальным оператором «И»:
Пароль длиннее 8 символов И пароль содержит хотя бы одну заглавную букву
Вот функция, которая принимает пароль и говорит, соответствует ли он условиям ( True ) или не соответствует ( False ):
def has_capital_letter(string): # Проверяет наличие хотя бы одной заглавной буквы в строке def is_correct_password(password): length = len(password) return length > 8 and has_capital_letter(password) print(is_correct_password('Qwerty')) # => False print(is_correct_password('Qwerty1234')) # => True print(is_correct_password('qwerty1234')) # => False
and — означает «И». В математической логике это называют конъюнкцией. Все выражение считается истинным, если истинен каждый операнд — каждое из составных выражений. Иными словами, and означает «и то, и другое». Приоритет этого оператора ниже, чем приоритет операторов сравнения. Поэтому выражение has_capital_letter(password) and length > 8 тоже правильно отрабатывает без скобок.
Кроме and часто используется оператор or — «ИЛИ» (дизъюнкция). Он означает «или то, или другое, или оба». Выражение a or b считается истинным, если хотя бы один из операндов или одновременно все — истинные. В другом случае выражение ложное.
Операторы можно комбинировать в любом количестве и любой последовательности. Если в коде одновременно встречаются and и or , то приоритет задают скобками. Ниже пример расширенной функции, которая определяет корректность пароля:
def has_capital_letter(string): # Проверяет наличие хотя бы одной заглавной буквы в строке def has_special_chars(string): # Проверяет содержание специальных символов в строке def is_strong_password(password): length = len(password) # Скобки задают приоритет. Понятно, что к чему относится. return (length > 8 and has_capital_letter(password)) and has_special_chars(password)
Теперь представим, что мы хотим купить квартиру, которая удовлетворяет таким условиям: площадь от 100 квадратных метров и больше на любой улице ИЛИ площадь от 80 квадратных метров и больше, но на центральной улице Main Street .
Напишем функцию, которая проверит квартиру. Она принимает два аргумента: площадь — число и название улицы — строку:
def is_good_apartment(area, street): return area >= 100 or (area >= 80 and street == 'Main Street') print(is_good_apartment(91, 'Queens Street')) # => False print(is_good_apartment(78, 'Queens Street')) # => False print(is_good_apartment(70, 'Main Street')) # => False print(is_good_apartment(120, 'Queens Street')) # => True print(is_good_apartment(120, 'Main Street')) # => True print(is_good_apartment(80, 'Main Street')) # => True
Область математики, в которой изучаются логические операторы, называется булевой алгеброй. Ниже увидите таблицы истинности — по ним можно определить, каким будет результат, если применить оператора:
И and
A | B | A and B |
---|---|---|
True | True | True |
True | False | False |
False | True | False |
False | False | False |
ИЛИ or
A | B | A or B |
---|---|---|
True | True | True |
True | False | True |
False | True | True |
False | False | False |
Задание
Реализуйте функцию is_leap_year() , которая принимает год в форме числа и определяет является ли он високосным или нет. Год будет високосным, если он кратен (то есть делится без остатка) 400 или он одновременно кратен 4 и не кратен 100. Как видите, в определении уже заложена вся необходимая логика, осталось только переложить её на код:
is_leap_year(2018) # false is_leap_year(2017) # false is_leap_year(2016) # true
Кратность можно проверять так:
# % - возвращает остаток от деления левого операнда на правый # Проверяем что number кратен 10 number % 10 == 0 # Проверяем что number не кратен 10 number % 10 != 0
Упражнение не проходит проверку — что делать?
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
- Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.
В моей среде код работает, а здесь нет
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Мой код отличается от решения учителя
Это нормально , в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Прочитал урок — ничего не понятно
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Полезное
- Булева алгебра
- Логическое «И»
- Логическое «ИЛИ»
Определения
- Логические операторы — операторы «И» ( and ), ИЛИ ( or ), позволяющие создавать составные логические условия.
1. Как решать задание ЕГЭ
7. Задание проверяет умение работать с логическими переменными, выполнять логические операции, строить таблицы истинности.
Пример задания
Николай заполнял таблицу истинности логической функции \(F\)
¬ ( y → ( x ≡ w ) ) ∧ ( z → x ) ,
но успел заполнить только фрагмент из трёх различных её строк, не указав, какому столбцу таблицы принадлежит каждая из переменных \(w\), \(x\), \(y\), \(z\).
Определи, какому столбцу таблицы принадлежит каждая из переменных \(w\), \(x\), \(y\), \(z\).
В ответе напиши буквы \(w\), \(x\), \(y\), \(z\) в том порядке, в котором идут соответствующие им столбцы (сначала буква, соответствующая первому столбцу; затем буква, соответствующая второму столбцу, и т. д.). Буквы в ответе пиши подряд, никаких разделителей между буквами ставить не нужно.
Как решать задание?
Данное задание можно решать несколькими способами: путём логических рассуждений; использовать электронные таблицы \(+\) логические рассуждения; с помощью программирования.
Мы рассмотрим вариант решения с помощью языка программирования Python.
Вспомнить основные логические операции можно тут.
Правила построения таблиц истинности можно вспомнить тут.
Законы алгебры логики можно вспомнить тут.
Вспомним управляющие конструкции в Python:
цикл с параметром
тело цикла
for \(y\) in \(0\), \(1\):
тело цикла
if условие then:
действия, если условие истинно
else:
действия, если условие ложно
if \(C>D\):
print (‘ истина ‘)
else:
print (‘ ложь ‘)
Запись логических операций на Python
Название операции
Запись на Python
конъюнкция
дизъюнкция
импликация
Напишем программу
Для каждой переменной сформируем вложенный цикл, где будем перебирать все возможные значения (\(0\) и \(1\)) для каждой переменной;
in range (\(2\)): перебор значений \(0\) и \(1\), можно записать по другому: in range (\(0\),\(2\))
Запуск программы
![]() |
Полученный результат необходимо сравнить с таблицей из условия |
Сопоставим полученную таблицу истинности с таблицей из условия. Нам необходимо сравнивать условие с результатами, как по строкам, так и по столбцам, отыскивая некоторые закономерности и невозможности. Заметим, что столбец \(y\) содержит в себе три единицы, что может быть только в третьем столбце таблицы из условия. Четвёртый столбик таблицы из условия также можем дополнить единицей, и к этому столбику подходят как \(z\), так и \(w\).
Теперь проанализируем строки. В первой строке получилось три единицы, что соответствует четвёртой строке результата программирования, дополняем нулём первую строку. В третьей строке у нас два нуля, поэтому добавляем единицу. Проверяем, чтобы сошлось по строке. Заполним второй столбец, допишем единицу. Этот столбец как раз и будет являться \(x\). Остаётся проанализировать столбцы \(1\) и \(4\).
Рассмотрим первую строку: \(x = 0\), \(y = 1\), \(z = 0\), следовательно, \(z\) — это четвёртый столбик, а \(w\) — первый.
python Разница между == (равно) и is (эквивалентность)
Из М. Лутца:
Первый способ, основанный на использовании оператора ==, проверяет, равны
ли значения объектов. В языке Python практически всегда используется именно этот способ.
Второй способ, основанный на использовании оператора is, проверяет идентичность объектов. Он возвращает значение True, только если оба имени ссылаются на один и тот же объект, вследствие этого он является более
строгой формой проверки равенства.
Key Words for FKN + antitotal forum (CS VSU):
Python: is. Равенство и эквивалентность
Новички часто путаются в конструкциях is и == . Давайте разберемся, что к чему.
Сразу к сути: == (и его антагонист != ) применяются для проверки равенства (неравенства) значения двух объектов. Значение, это непосредственно то, что лежит в переменной. Значение числа 323235 – собственно число 323235. Тавтология. Но на примерах станет яснее.
Оператор is (и его антагонист is not ) применяются проверки равенства (неравенства) ссылок на объект. Сразу отметим то, что на значение (допустим 323235) может быть копировано и храниться в разных местах (в разных объектах в памяти).
>> x = 323235 >> y = 323235 >> x == y True >> x is y False
Видите, значение переменных равны по значению, но они ссылаются на разные объекты. Я не случайно взял большое число 323235. Дело в том, что в целях оптимизации интерпретатор Python при старте создает некоторые количество часто-используемых констант (от -5 до 256 включительно).
Следите внимательно за ловкостью рук:
>>> x = 256 >>> y = 256 >>> x is y True >>> x = 257 >>> y = 257 >>> x is y False >>> x = -5 >>> y = -5 >>> x is y True >>> x = -6 >>> y = -6 >>> x is y False
Поэтому новички часто совершают ошибку, считая, что писать == – это как-то не Python-way, а is – Python-way. Это ошибочное предположение может быть раскрыто не сразу.
Python старается кэшировать и переиспользовать строковые значения. Поэтому весьма вероятно, что переменные, содержащие одинаковые строки, будут содержать ссылки на одинаковые объекты. Но это не факт! Смотрите последний пример:
>>> x = "hello" >>> y = "hello" >>> x is y True >>> x = "hel" + "lo" >>> y = "hello" >>> x is y True >>> a = "hel" >>> b = "lo" >>> x = a + b >>> y = "hello" >>> x == y True >>> x is y False
Мы составили строку из двух частей и она попала в другой объект. Python не догадался (и правильно) поискать ее в существующих строках.
Суть is (id)
В Python есть встроенная функция id . Она возвращает идентификатор объекта – некоторое число. Гарантируется, что оно будет различно для различных объектах в пределах одного интерпретатора. В реализации CPython – это просто адрес объекта в памяти интерпретатора.
a is b
Это тоже самое, что:
id(a) == id(b)
И все! Пример для проверки:
>>> x = 10.40 >>> y = 10.40 >>> x is y False >>> x == y True >>> id(x) 4453475504 >>> id(y) 4453475600 >>> id(x) == id(y) False >>> x = y >>> x is y True >>> id(x) 4453475600 >>> id(y) 4453475600
Значения переменных равны, но их id – разные, и is выдает False . Как только мы к x привязали y , то ссылки стали совпадать.
Для чего можно применять is?
Если мы точно знаем уверены, что хотим проверять именно равенство ссылок на объекты (один ли это объект в памяти или разные).
Еще можно применять is для сравнения с None . None – это встроенная константа и двух None быть не может.
>>> x is None False >>> x = None >>> x is None True
Также для Ellipsis:
>>> . is Ellipsis True >>> x = . >>> y = . >>> x is y True
Я не рекомендую применять is для True и False .
Потому что короче писать if x: , чем if x is True: .
Можно применять is для сравнения типов с осторожностью (без учета наследования, т. е. проверка на точное совпадение типов):
>>> x = 10.5 >>> type(x) is float True
С наследованием может быть конфуз:
>>> class Foo: . . >>> class Bar(Foo): . . >>> f = Foo() >>> b = Bar() >>> type(f) is Foo True >>> type(b) is Bar True >>> type(b) is Foo False >>> isinstance(b, Foo) True
Не смотря на то, что Bar – наследник Foo , типы переменных foo и bar не совпадают. Если нам важно учесть наcледование, то пишите isinstance .
Нюанс: is not против is (not)
Важно знать, что is not – это один целый оператор, аналогичный id(x) != id(y) . А в конструкции x is (not y) – у нас сначала будет логическое отрицание y , а потом просто оператор is .
>>> x = 10 >>> x is not None True >>> x is (not None) False
Сравнение пользовательских классов
Далее речь пойдет об обычных == и != . Можно определить магический метод __eq__ , который обеспечит поведение при сравнении классов. Если он не реализован, то объекты будет сравниваться по ссылкам (как при is ).
>>> class Baz: . . >>> x = Baz() >>> y = Baz() >>> x == y False >>> x = y >>> x == y True
Если он реализован, то будет вызван метод __eq__ для левого операнда.
class Foo: def __init__(self, x): self.x = x def __eq__(self, other): print('Foo __eq__ <> and <>'.format(self, other)) return self.x == other.x >>> x = Foo(5) >>> y = Foo(5) >>> x == y Foo __eq__ and True
Метод __ne__ отвечает за реализацию != . По умолчанию он вызывает not x.__eq__(y) . Но рекомендуется реализовывать их оба вручную, чтобы поведение сравнения было согласовано и явно.
Вопрос к размышлению: что будет если мы сравним объекты разных классов, причем оба класса реализуют __eq__ ?
Что будет, если мы реализуем __ne__ , но не реализуем __eq__ ?
А еще есть метод __cmp__ . Это уже выходит за рамки статьи про is . Почитайте самостоятельно…