Каппес Даниил Олегович

ПРАКТИКУМ PYTHON · ЗАНЯТИЕ 11

Булевы функции и возврат нескольких значений

Функция отвечает «да» или «нет» — и сложное условие превращается в одну понятную строку. А ещё возвращает сразу несколько значений.

Чему научимся

Функции, отвечающие «да» или «нет»

Функция может возвращать не только число или строку, но и логическое значение — True либо False. Такие функции называют булевыми и обычно называют так, чтобы вызов читался как вопрос.

number = int(input())
if number % 2 == 0:
    print('Это число чётное.')
else:
    print('Это число нечётное.')

Тот же код с булевой функцией:

def is_even(number):
    if number % 2 == 0:
        return True
    else:
        return False

number = int(input())
if is_even(number):
    print('Это число чётное.')
else:
    print('Это число нечётное.')

Строка if is_even(number): читается вслух: «если число чётное». Вызов функции сам по себе даёт True или False, поэтому сравнивать его с чем-то (== True) не нужно.

Особенно хорошо это видно на проверке ввода. Вот цикл, который требует ввести номер модели из трёх допустимых:

model = int(input())
while model != 100 and model != 200 and model != 300:
    print('Допустимы модели 100, 200 и 300.')
    model = int(input())

И он же с булевой функцией:

def is_invalid(model):
    if model != 100 and model != 200 and model != 300:
        return True
    else:
        return False

model = int(input())
while is_invalid(model):
    print('Допустимы модели 100, 200 и 300.')
    model = int(input())

Условие никуда не делось — оно переехало внутрь функции. Но теперь заголовок цикла говорит, что проверяется, а не как, и читать его можно, не разбираясь в деталях.

Заводить функцию ради одной короткой проверки не стоит: код длиннее, а вызов функции — это ещё и время. Булева функция окупается, когда условие длинное или проверять его надо в нескольких местах.

11.1

Простое ли число

Напишите функцию is_prime(num), которая принимает натуральное число и возвращает True, если оно простое, и False в противном случае.

Простое число делится только на единицу и на само себя. Единица не простая и не составная.

Формат входных данных
Одно натуральное число.
Формат выходных данных
True или False.

Тестовые данные

ВводВывод
11False
210False
32True
417True
5101True
6100False
7360000False
8121False
Подсказка 1

Проверка та же, что и на четвёртом занятии: перебрать возможные делители от 2 до num − 1.

Подсказка 2

Внутри функции сигнальная метка не нужна: как только делитель нашёлся, можно сразу вернуть Falsereturn завершает функцию.

Разбор
  1. Числа меньше двух простыми не бывают — отвечаем False сразу.
  2. Перебираем делители от 2 до num − 1.
  3. Нашёлся делитель — возвращаем False.
  4. Цикл закончился, а делителей нет — значит, число простое: возвращаем True.
Решение
def is_prime(num):
    if num < 2:              # единица и всё, что меньше, — не простые
        return False

    for d in range(2, num):
        if num % d == 0:     # нашёлся делитель — дальше не смотрим
            return False

    return True              # делителей не нашлось

# основная программа
print(is_prime(int(input())))

return внутри цикла заменил и сигнальную метку, и break — он делает то же самое, только сразу. Это типичное упрощение, которое появляется, как только код переезжает в функцию.

11.2

Следующее простое

Напишите функцию get_next_prime(num), которая принимает натуральное число и возвращает первое простое число, большее данного.

Используйте функцию is_prime() из предыдущей задачи.

Формат входных данных
Одно натуральное число.
Формат выходных данных
Ближайшее большее простое число.

Тестовые данные

ВводВывод
167
2711
31417
412
523
697101
Подсказка 1

Сколько чисел придётся перебрать, заранее неизвестно — значит, while.

Подсказка 2

Начинать надо со следующего числа: у семёрки ответ 11, а не она сама.

Разбор
  1. Берём число на единицу больше данного.
  2. Пока оно не простое — увеличиваем на единицу.
  3. Как только is_prime() ответил True, возвращаем его.
Решение
def is_prime(num):
    if num < 2:
        return False
    for d in range(2, num):
        if num % d == 0:
            return False
    return True

def get_next_prime(num):
    candidate = num + 1              # само число не считается
    while not is_prime(candidate):   # пока не простое — идём дальше
        candidate += 1
    return candidate

# основная программа
print(get_next_prime(int(input())))

while not is_prime(candidate) читается прямо: «пока кандидат не простой». Без булевой функции в заголовке цикла пришлось бы городить перебор делителей — прямо там, где важна только суть.

11.3

Надёжный пароль

Напишите функцию is_password_good(password), которая принимает строку с паролем и возвращает True, если пароль надёжный, и False в противном случае. Пароль надёжен, если:

  • его длина не менее 8 символов;
  • он содержит хотя бы одну заглавную букву;
  • он содержит хотя бы одну строчную букву;
  • он содержит хотя бы одну цифру.
Формат входных данных
Одна строка — пароль.
Формат выходных данных
True или False.
Ввод 1
aabbCC11OP
Вывод 1
True
Ввод 2
abC1pu
Вывод 2
False
Ввод 3
abcdefgh
Вывод 3
False
Подсказка 1

Длину можно проверить сразу и, если она мала, вернуть False, не проверяя остальное.

Подсказка 2

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

Разбор
  1. Если длина меньше восьми — сразу False.
  2. Заводим три флажка со значением False.
  3. Проходим по символам и поднимаем нужный флажок, встретив заглавную, строчную или цифру.
  4. Возвращаем True, только если подняты все три.
Решение
def is_password_good(password):
    if len(password) < 8:
        return False

    has_upper = False
    has_lower = False
    has_digit = False

    for c in password:
        if c.isupper():
            has_upper = True
        if c.islower():
            has_lower = True
        if c.isdigit():
            has_digit = True

    return has_upper and has_lower and has_digit

# основная программа
print(is_password_good(input()))

Последняя строка возвращает результат логического выражения — не True или False по отдельности, а сразу их связку. Писать if has_upper and … : return True else: return False можно, но это тот же ответ на две строки длиннее.

11.4

Отличаются ровно в одном

Напишите функцию is_one_away(word1, word2), которая принимает два слова и возвращает True, если они одинаковой длины и отличаются ровно одним символом, и False в противном случае.

Формат входных данных
Две строки — слова.
Формат выходных данных
True или False.

Тестовые данные

ВводВывод
1bike hikeTrue
2water waferTrue
3abcd abpoFalse
4abcd abcdeFalse
5abcd1234567 abcd1234568True
6abcd abcdFalse
7aab abaFalse
Подсказка 1

Слова разной длины сравнивать посимвольно бессмысленно — эту проверку делают первой.

Подсказка 2

Дальше считают, сколько позиций различаются, и сравнивают счётчик с единицей.

Разбор
  1. Если длины разные — сразу False.
  2. Заводим счётчик различий и идём по индексам.
  3. Каждое несовпадение увеличивает счётчик.
  4. Возвращаем результат сравнения diff == 1.
Решение
def is_one_away(word1, word2):
    if len(word1) != len(word2):    # разной длины — сравнивать нечего
        return False

    diff = 0
    for i in range(len(word1)):
        if word1[i] != word2[i]:
            diff += 1

    return diff == 1                # ровно одно различие

# основная программа
print(is_one_away(input(), input()))

Последняя строка — это выражение сравнения, а оно само по себе даёт True или False. Такой возврат короче развилки и читается не хуже: «вернуть, равно ли число различий единице».

Несколько значений сразу

В Python функция не ограничена одним возвращаемым значением: после return их можно перечислить через запятую.

def get_powers(num):
    return num ** 2, num ** 3, num ** 4

a, b, c = get_powers(2)
print(a, b, c)

Результат

4 8 16

Слева от = при этом стоит столько же имён, сколько значений вернула функция, — это распаковка, тот же приём, что и в обмене x, y = y, x.

Это редкая особенность: в большинстве языков, чтобы вернуть несколько значений, приходится собирать их в список.

Вот функция, решающая систему двух линейных уравнений и возвращающая обе координаты точки пересечения:

def solve(a, b, c, d, e, f):
    x = (d * e - b * f) / (a * d - b * c)
    y = (a * f - c * e) / (a * d - b * c)
    return x, y

xsol, ysol = solve(2, 3, 4, 1, 2, 5)
print('Решением системы являются числа', 'x =', xsol, 'y =', ysol)

Результат

Решением системы являются числа x = 1.3 y = -0.2

Результат можно и не распаковывать: print(solve(...)) напечатает оба значения в скобках через запятую. А print(*solve(...)) — через пробел, распаковкой, как список.

11.5

Середина отрезка

Напишите функцию get_middle_point(x1, y1, x2, y2), которая принимает координаты концов отрезка и возвращает координаты его середины.

Координаты середины считаются как полусуммы: ((x₁ + x₂) / 2; (y₁ + y₂) / 2).

Формат входных данных
Четыре числа — координаты концов, каждое на отдельной строке.
Формат выходных данных
Две координаты середины через пробел.

Тестовые данные

ВводВывод
11 1 2 21.5 1.5
2-10 10 0 100-5.0 55.0
310 10 10 2010.0 15.0
Подсказка 1

Функция должна вернуть два значения — перечислите их после return через запятую.

Подсказка 2

Чтобы напечатать оба через пробел, пригодится распаковка print(*…).

Разбор
  1. Считаем полусумму по каждой координате.
  2. Возвращаем оба значения одной инструкцией return.
  3. В основной программе печатаем результат распаковкой.
Решение
def get_middle_point(x1, y1, x2, y2):
    return (x1 + x2) / 2, (y1 + y2) / 2   # два значения через запятую

# основная программа
x1 = int(input())
y1 = int(input())
x2 = int(input())
y2 = int(input())

print(*get_middle_point(x1, y1, x2, y2))

Деление здесь обычное, а не целочисленное: середина отрезка из целых точек запросто бывает дробной. Поэтому и в ответе 5.0, а не 5.

11.6

Площадь и длина

Напишите функцию get_circle(radius), которая принимает радиус окружности и возвращает два значения: длину окружности и площадь ограниченного ею круга.

Длина окружности — 2πr, площадь круга — πr². Для числа π используйте константу из модуля math.

Формат входных данных
Одно число — радиус.
Формат выходных данных
Два числа через пробел: длина окружности и площадь.

Тестовые данные

ВводВывод
116.283185307179586 3.141592653589793
27.245.23893421169302 162.8601631620949
31.59.42477796076938 7.0685834705770345
Подсказка 1

Порядок важен: сначала длина окружности, потом площадь.

Подсказка 2

π берут из math — своё приближение писать не нужно.

Разбор
  1. Подключаем модуль math.
  2. Возвращаем два выражения через запятую: 2 * math.pi * radius и math.pi * radius ** 2.
  3. Читаем радиус как вещественное число и печатаем результат распаковкой.
Решение
import math

def get_circle(radius):
    return 2 * math.pi * radius, math.pi * radius ** 2

# основная программа
r = float(input())
print(*get_circle(r))

Читаем радиус через float(), а не int(): во втором примере он равен 1.5. Мелочь, которая ломает программу мгновенно и всегда — стоит сразу смотреть на примеры, прежде чем выбирать преобразование.

Итог занятия

Главное с одиннадцатого занятия. Булева функция возвращает True или False, и её вызов можно ставить прямо в условие — сравнивать с True не нужно. Возвращать результат сравнения (return diff == 1) короче, чем развилку из двух return. А функция, вернувшая несколько значений, разбирается распаковкой: x, y = get_middle_point(…).

На этом теоретическая часть курса заканчивается. Дальше — справочник по модулю random и три проекта, в которых всё изученное собирается в настоящие программы.