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

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

Условный оператор, логические операторы

Программа перестаёт идти по прямой: появляется развилка — «если …, то …, иначе …».

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

Сначала повторим

Прошлое занятие держится на трёх вещах: input() читает строку, int() превращает её в число, а // и % отвечают на вопросы «сколько раз поместилось» и «что осталось». С этого и начнём.

2.0

Число по цифрам

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

Для решения понадобятся целочисленное деление // и остаток % — ничего нового.

Формат входных данных
Одно целое четырёхзначное число, например 1852.
Формат выходных данных
Четыре строки: первая цифра числа, вторая, третья, четвёртая.
Ввод
1852
Вывод
1
8
5
2
Подсказка 1

Последняя цифра — это остаток от деления на 10. А как добраться до предпоследней?

Подсказка 2

Деление нацело на 10 отбрасывает последнюю цифру: 1852 // 10 — это 185. Значит, любую цифру можно сначала «выдвинуть» в конец, а потом взять остатком.

Разбор
  1. Первая цифра — n // 1000: тысячи целиком.
  2. Вторая — сначала отбрасываем две последние цифры (n // 100), потом берём остаток: n // 100 % 10.
  3. Третья — то же самое, но отбрасываем одну цифру: n // 10 % 10.
  4. Четвёртая — просто остаток: n % 10.
Решение
n = int(input())

print(n // 1000)      # тысячи
print(n // 100 % 10)  # выдвинули сотни в конец и взяли остаток
print(n // 10 % 10)   # то же с десятками
print(n % 10)         # единицы — просто остаток

Приём «поделить нацело, потом взять остаток» вытаскивает любую цифру числа. Он ещё пригодится: на пятом занятии им же будем разбирать число по цифрам в цикле.

Развилка: if и else

Проверку условия в Python начинает ключевое слово if. Двоеточие в конце строки говорит, что дальше идёт блок команд — то, что выполнится, если условие верно.

answer = input('Какой язык программирования мы изучаем? ')
if answer == 'Python':
    print('Верно! Мы ботаем Python =)')
    print('Python — отличный язык!')

Отступ — часть кода, а не украшение. Именно он говорит, где блок начинается и где заканчивается: в блок входят все строки со сдвигом вправо под if, вплоть до первой строки без отступа. По соглашению PEP 8 отступ — четыре пробела; смешивать пробелы и табуляцию нельзя.

Если нужно ещё и «а иначе», добавляют else:

if answer == 'Python':
    print('Верно!')
else:
    print('Не угадал')

Одиночное = — это присваивание, а сравнение — двойное ==. if x = 5: не заработает вовсе, и это к лучшему: язык не даёт перепутать их молча.

Шесть сравнений и цепочки

ЗаписьЧитается
x > 7x больше 7
x < 7x меньше 7
x >= 7x больше либо равен 7
x <= 7x меньше либо равен 7
x == 7x равен 7
x != 7x не равен 7

Сравнения можно связывать в цепочку — так, как пишут в математике. В большинстве языков программирования этого нет, и там приходится городить связки:

age = int(input())
if 3 <= age <= 6:
    print('Вы ребёнок')

Так же проверяется и равенство трёх значений сразу:

if a == b == c:
    print('числа равны')
else:
    print('числа не равны')

Равенство транзитивно: если a == b и b == c, то a == c. А вот неравенство — нет: из a != b и b != c вовсе не следует, что a != c. Цепочка a != b != c поэтому обманчива, и лучше её не писать.

2.1

Пароль и подтверждение

При регистрации на сайтах пароль просят ввести дважды: так меньше шансов ошибиться. Напишите программу, которая сравнивает пароль и его подтверждение. Если они совпадают, программа выводит «Пароль принят», иначе — «Пароль не принят».

Формат входных данных
Две строки.
Формат выходных данных
Одна строка в соответствии с условием.
Ввод 1
qwerty
qwerty
Вывод 1
Пароль принят
Ввод 2
qwerty
qwerty1
Вывод 2
Пароль не принят
Подсказка 1

Строки сравниваются тем же ==, что и числа.

Подсказка 2

Переводить введённое в число здесь не нужно — пароль и есть текст.

Разбор
  1. Читаем две строки в две переменные.
  2. Сравниваем их: совпали — печатаем одно, не совпали — другое.
Решение
first = input()
second = input()

if first == second:      # у строк == работает так же, как у чисел
    print('Пароль принят')
else:
    print('Пароль не принят')

Сравнение строк — посимвольное и с учётом регистра: 'Qwerty' и 'qwerty' не равны. Для пароля это ровно то, что нужно.

2.2

Наименьшее из двух

Напишите программу, которая определяет наименьшее из двух чисел.

Формат входных данных
Два различных целых числа, каждое на отдельной строке.
Формат выходных данных
Наименьшее из двух чисел.
Ввод
17
5
Вывод
5
Подсказка 1

Развилка тут ровно одна: либо первое меньше, либо второе.

Подсказка 2

Печатать можно прямо в каждой ветке — заводить лишнюю переменную не обязательно.

Разбор
  1. Сравниваем первое со вторым.
  2. Если первое меньше — печатаем его, иначе печатаем второе.
Решение
a = int(input())
b = int(input())

if a < b:
    print(a)
else:
    print(b)

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

2.3

Возрастная группа

Напишите программу, которая по введённому возрасту сообщает, к какой возрастной группе относится человек:

  • до 13 включительно — детство
  • от 14 до 24 — молодость
  • от 25 до 59 — зрелость
  • от 60 — старость
Формат входных данных
Одно целое число — возраст.
Формат выходных данных
Название возрастной группы.
Ввод 1
13
Вывод 1
детство
Ввод 2
24
Вывод 2
молодость
Ввод 3
60
Вывод 3
старость
Подсказка 1

Групп четыре, а значит, проверок нужно три: последняя ветка достаётся всем оставшимся.

Подсказка 2

Если проверять по порядку, снизу вверх, то каждая следующая проверка уже знает, что предыдущая не сработала.

Разбор
  1. Первая проверка: age <= 13 — детство.
  2. Вторая: age <= 24. Сюда попадут только те, кому больше 13, — значит, писать 14 <= age <= 24 не нужно.
  3. Третья: age <= 59. Всё, что осталось, — старость.
Решение
age = int(input())

if age <= 13:
    print('детство')
elif age <= 24:      # сюда попадут только те, кому больше 13
    print('молодость')
elif age <= 59:
    print('зрелость')
else:
    print('старость')

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

Связки and, or, not

Условия соединяются тремя логическими операторами: and — логическое умножение («и»), or — логическое сложение («или»), not — отрицание («не»).

aba and ba or b
FalseFalseFalseFalse
FalseTrueFalseTrue
TrueFalseFalseTrue
TrueTrueTrueTrue

Проще всего запомнить так: and истинно, только когда истинны оба условия; or ложно, только когда ложны оба. А not просто переворачивает значение:

anot a
FalseTrue
TrueFalse

Порядок выполнения: сначала not, потом and, потом or. Если нужен другой порядок — ставят скобки. Скобки не помешают и там, где порядок и так верный: условие 1000 <= n <= 9999 and (n % 7 == 0 or n % 17 == 0) без них читалось бы совсем иначе.

2.4

Принадлежит ли промежутку

Напишите программу, которая принимает целое число x и определяет, принадлежит ли оно промежутку (−1; 17).

Программа должна вывести «Принадлежит» или «Не принадлежит».

Скобки в записи промежутка круглые — значит, сами границы в него не входят: −1 и 17 промежутку не принадлежат.

Формат входных данных
Целое число x.
Формат выходных данных
Одно слово в соответствии с условием.
Ввод 1
2
Вывод 1
Принадлежит
Ввод 2
-790
Вывод 2
Не принадлежит
Ввод 3
16
Вывод 3
Принадлежит
Ввод 4
17
Вывод 4
Не принадлежит
Подсказка 1

Условий на самом деле два: число больше левой границы и меньше правой.

Подсказка 2

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

Разбор
  1. Границы в промежуток не входят, поэтому знаки строгие: <, а не <=.
  2. Цепочка -1 < x < 17 читается ровно как запись из условия.
Решение
x = int(input())

if -1 < x < 17:      # круглые скобки в условии — знаки строгие
    print('Принадлежит')
else:
    print('Не принадлежит')

Всё решают знаки: у промежутка (−1; 17) они строгие, а у [−1; 17] были бы нестрогими. Проверяется это ровно на границах — 17 в примерах стоит именно для этого. Через связку то же условие пишется как x > -1 and x < 17: верно, но длиннее.

2.5

Два промежутка

Напишите программу, которая принимает целое число x и определяет, принадлежит ли оно объединению промежутков (−∞; −3] и [7; +∞) — то есть всему, что левее −3 или правее 7, включая сами границы.

Программа должна вывести «Принадлежит» или «Не принадлежит».

Формат входных данных
Целое число x.
Формат выходных данных
Одно слово в соответствии с условием.
Ввод 1
-44
Вывод 1
Принадлежит
Ввод 2
-3
Вывод 2
Принадлежит
Ввод 3
0
Вывод 3
Не принадлежит
Подсказка 1

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

Подсказка 2

«Хотя бы в один» — это or, а не and. Условие с and не выполнится никогда: одно число не может быть одновременно меньше −3 и больше 7.

Разбор
  1. Первый промежуток: x <= -3. Второй: x >= 7.
  2. Соединяем их через or — годится любой из двух.
Решение
x = int(input())

# годится любой из промежутков, поэтому or, а не and
if x <= -3 or x >= 7:
    print('Принадлежит')
else:
    print('Не принадлежит')

Проверить себя просто: подставьте ноль. С or получится «Не принадлежит» — верно; с and программа отвечала бы «Не принадлежит» вообще всегда, и на этом примере ошибку было бы не видно.

2.6

Красивое число

Назовём число красивым, если оно четырёхзначное и делится нацело на 7 или на 17. Напишите программу, определяющую, является ли введённое число красивым: она выводит «YES» или «NO».

Формат входных данных
Натуральное число.
Формат выходных данных
YES или NO.
Ввод 1
1043
Вывод 1
YES
Ввод 2
1045
Вывод 2
NO
Ввод 3
7
Вывод 3
NO
Подсказка 1

«Делится нацело на 7» — это остаток от деления на 7 равен нулю.

Подсказка 2

Условий три: четырёхзначность и две делимости. Одно из них обязательно, а из двух других достаточно любого — тут и понадобятся скобки.

Разбор
  1. Четырёхзначное — значит, от 1000 до 9999: цепочка 1000 <= n <= 9999.
  2. Делимость: n % 7 == 0 или n % 17 == 0.
  3. Скобки обязательны: без них and сработает раньше or, и число 7 сойдёт за красивое.
Решение
n = int(input())

# скобки обязательны: без них and выполнится раньше or
if 1000 <= n <= 9999 and (n % 7 == 0 or n % 17 == 0):
    print('YES')
else:
    print('NO')

Без скобок условие читается как «(четырёхзначное и делится на 7) или (делится на 17)» — и однозначное 17 объявляется красивым. Ошибка тихая: на большинстве примеров ответ совпадает.

2.7

Ход короля

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

На вход подаются четыре числа от 1 до 8: номер столбца и номер строки сначала для первой клетки, потом для второй. Программа должна вывести «YES» или «NO».

Король ходит по горизонтали, вертикали и диагонали, но только на одну клетку.

1234567812345678
Король стоит на клетке (4; 4). Одним ходом он попадает в любую из восьми соседних — и номер столбца, и номер строки меняются не больше чем на единицу.
Формат входных данных
Четыре числа от 1 до 8, каждое на отдельной строке.
Формат выходных данных
YES или NO.
Ввод 1
4
4
5
5
Вывод 1
YES
Ввод 2
4
4
4
6
Вывод 2
NO
Подсказка 1

Король попадает туда, где номер столбца отличается не больше чем на единицу и номер строки — тоже.

Подсказка 2

«Отличается не больше чем на 1» — это разность от −1 до 1. А такое проверяется цепочкой.

Разбор
  1. Разность столбцов должна лежать в промежутке от −1 до 1: -1 <= x1 - x2 <= 1.
  2. То же самое для строк.
  3. Оба условия обязательны — соединяем через and.
Решение
x1 = int(input())
y1 = int(input())
x2 = int(input())
y2 = int(input())

# ход короля: и столбец, и строка меняются не больше чем на единицу
if -1 <= x1 - x2 <= 1 and -1 <= y1 - y2 <= 1:
    print('YES')
else:
    print('NO')

Разбирать восемь направлений по отдельности не нужно — все они укладываются в два условия на разности. На следующем занятии появится abs(), и запись станет ещё короче: abs(x1 - x2) <= 1.

Вложенная развилка и каскад elif

Внутри блока if может стоять что угодно — в том числе ещё одна развилка. Тогда после первой развилки появляется вторая, и отступ каждого следующего уровня растёт ещё на четыре пробела.

Когда проверок много, вложенность становится неудобной, и вместо неё пишут каскад:

grade = int(input('Введите вашу отметку: '))
if grade >= 90:
    print(5)
elif grade >= 80:
    print(4)
elif grade >= 70:
    print(3)
elif grade >= 60:
    print(2)
else:
    print(1)

Условия проверяются сверху вниз. Как только нашлось истинное, выполняется его блок, а вся остальная часть каскада пропускается. Если не сработало ни одно условие, выполняется блок else.

Каскад можно было бы записать вложенными if-else, но длинная лесенка if-else плоха вдвойне: её тяжелее читать и она уезжает вправо, за край экрана. В каскаде все if, elif и else стоят на одном уровне, и строки получаются короче.

Ещё две мелочи, которые пригодятся дальше. Множественное присваивание — несколько имён за одну строку:

a, b, c = int(input()), int(input()), int(input())

И обмен значений без третьей переменной — приём, которого нет в большинстве языков:

x, y = y, x
2.8

Вид треугольника

Напишите программу, которая принимает три положительных числа — длины сторон существующего треугольника — и определяет его вид: «Равносторонний», «Равнобедренный» или «Разносторонний».

Формат входных данных
Три числа — длины сторон, каждое на отдельной строке.
Формат выходных данных
Вид треугольника с заглавной буквы.
Ввод 1
145
145
139
Вывод 1
Равнобедренный
Ввод 2
59
59
59
Вывод 2
Равносторонний
Ввод 3
3
4
5
Вывод 3
Разносторонний
Подсказка 1

Проверять надо в правильном порядке: равносторонний треугольник тоже равнобедренный, а наоборот — не всегда.

Подсказка 2

Равенство трёх сторон проверяется цепочкой a == b == c, а «хотя бы две равны» — тремя сравнениями через or.

Разбор
  1. Сначала самый строгий случай: все три стороны равны — равносторонний.
  2. Потом «хотя бы две равны»: a == b or a == c or b == c. Сюда попадут только те, кто не прошёл первую проверку.
  3. Всё остальное — разносторонний.
Решение
a, b, c = int(input()), int(input()), int(input())

if a == b == c:                      # сначала самый строгий случай
    print('Равносторонний')
elif a == b or a == c or b == c:     # хотя бы две стороны равны
    print('Равнобедренный')
else:
    print('Разносторонний')

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

2.9

Самописный калькулятор

Напишите программу, которая считывает два целых числа и строку. Если строка — обозначение одной из четырёх операций (+, -, *, /), выведите результат применения этой операции к введённым числам, иначе — «Неверная операция». Если пользователь захочет поделить на ноль, выведите «На ноль делить нельзя!».

Формат входных данных
Два целых числа, каждое на отдельной строке, и строка с операцией.
Формат выходных данных
Результат или соответствующее сообщение.
Ввод 1
6104
0
/
Вывод 1
На ноль делить нельзя!
Ввод 2
25
5
*
Вывод 2
125
Ввод 3
25
5
^
Вывод 3
Неверная операция
Подсказка 1

Операций четыре, плюс случай «ничего из перечисленного» — это ровно каскад из четырёх проверок и else.

Подсказка 2

Деление на ноль — не пятая операция, а особый случай внутри деления. Значит, эта проверка живёт внутри ветки /.

Разбор
  1. Читаем два числа и строку с операцией.
  2. Каскад: +, -, *, /, иначе — «Неверная операция».
  3. Внутри ветки деления — своя развилка: если второе число ноль, печатаем предупреждение, иначе делим.
Решение
a = int(input())
b = int(input())
operation = input()

if operation == '+':
    print(a + b)
elif operation == '-':
    print(a - b)
elif operation == '*':
    print(a * b)
elif operation == '/':
    if b == 0:                       # вложенная развилка: только для деления
        print('На ноль делить нельзя!')
    else:
        print(a / b)
else:
    print('Неверная операция')

Проверку b == 0 нельзя вынести наверх: при умножении на ноль никакой ошибки нет, и программа зря ругалась бы. Вложенность здесь не украшение, а точное место проверки.

2.10

Пересечение отрезков

На числовой прямой даны два отрезка: [a₁; b₁] и [a₂; b₂]. Напишите программу, которая находит их пересечение. Пересечением может быть отрезок, точка или пустое множество.

Гарантируется, что a₁ < b₁ и a₂ < b₂.

Формат входных данных
Четыре целых числа a₁, b₁, a₂, b₂, каждое на отдельной строке.
Формат выходных данных
Границы отрезка через пробел, либо одна общая точка, либо текст «пустое множество».
Ввод 1
1
3
2
4
Вывод 1
2 3
Ввод 2
1
2
3
4
Вывод 2
пустое множество
Ввод 3
5
6
6
8
Вывод 3
6
Подсказка 1

Нарисуйте два отрезка на прямой. Где начинается их общая часть — в левом из начал или в правом?

Подсказка 2

Начало пересечения — правое из двух начал, конец — левое из двух концов. Дальше остаётся сравнить их между собой.

Разбор
  1. Находим начало пересечения: большее из a₁ и a₂.
  2. Находим конец: меньшее из b₁ и b₂.
  3. Если начало меньше конца — это отрезок; если равно — точка; если больше — отрезки не пересекаются.
Решение
a1 = int(input())
b1 = int(input())
a2 = int(input())
b2 = int(input())

# начало пересечения — правое из начал
if a1 > a2:
    left = a1
else:
    left = a2

# конец пересечения — левое из концов
if b1 < b2:
    right = b1
else:
    right = b2

if left < right:
    print(left, right)
elif left == right:
    print(left)
else:
    print('пустое множество')

Разбирать взаимное расположение отрезков случай за случаем (один внутри другого, налезают слева, справа…) — долго и легко что-нибудь забыть. Приём «правое из начал, левое из концов» покрывает все случаи сразу. На следующем занятии он сократится до двух строк с max() и min().

2.11

Ход слона

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

На вход подаются четыре числа от 1 до 8: столбец и строка первой клетки, затем второй. Вывести нужно «YES» или «NO».

Слон ходит по диагоналям. Через клетку (x₀; y₀) проходят ровно две диагонали: одна под углом 45°, другая под −45°. На первой у всех клеток одинакова разность x − y, на второй — сумма x + y.

1234567812345678
Слон стоит на клетке (4; 4) и ходит по двум диагоналям. У клеток восходящей диагонали одинакова разность «столбец минус строка», у нисходящей — сумма.
Формат входных данных
Четыре числа от 1 до 8, каждое на отдельной строке.
Формат выходных данных
YES или NO.
Ввод 1
4
4
5
5
Вывод 1
YES
Ввод 2
4
4
5
4
Вывод 2
NO
Ввод 3
1
8
8
1
Вывод 3
YES
Подсказка 1

Выпишите координаты нескольких клеток одной диагонали: (1;1), (2;2), (3;3). Что у них общего?

Подсказка 2

У клеток восходящей диагонали одинакова разность x - y, у нисходящей — сумма x + y. Достаточно любого совпадения.

Разбор
  1. Восходящая диагональ: x1 - y1 == x2 - y2.
  2. Нисходящая: x1 + y1 == x2 + y2.
  3. Слон ходит по любой из двух — соединяем через or.
Решение
x1 = int(input())
y1 = int(input())
x2 = int(input())
y2 = int(input())

# на одной диагонали совпадает либо разность координат, либо сумма
if x1 - y1 == x2 - y2 or x1 + y1 == x2 + y2:
    print('YES')
else:
    print('NO')

Диагональ — это прямая y = x + b или y = -x + b. Проверка на равенство разностей и сумм — это и есть проверка «лежат ли обе клетки на одной такой прямой», только без единого умножения.

Целые и вещественные

Целые числа в Python — тип int (от integer, целое). Число, записанное в коде прямо, называют литералом: встретив n = 17, Python создаёт объект типа int со значением 17.

Целые числа в Python не ограничены. Размер зависит только от свободной памяти — в отличие от C++, C#, Java, где у целого типа есть потолок.

atom = 10 ** 80        # примерное число атомов во Вселенной
print(atom)

Результат

1000000000000000000000000000000000000000000000000000000000000000000000000000000000

Длинные числа удобно разделять подчёркиванием — на значение это не влияет:

num = 25_000_000
print(num)

Результат

25000000

Числа с дробной частью — тип float. В коде дробная часть отделяется точкой, а не запятой: e = 2.71828. Строку в такое число превращает float().

Преобразование float в int округляет в сторону нуля, а не по правилам математики: int(1.7) — это 1, а int(-1.7) — это −1.

И ещё одно свойство float, которое видно сразу: дробные числа хранятся в двоичном виде, и не каждая привычная дробь записывается в нём точно. Поэтому результат вычислений иногда обрастает хвостом из нулей и случайной цифры на конце — например, 44.45 - 44 даёт 0.45000000000000284. Это не ошибка программы, а свойство самого типа.

Хвост убирают округлением — встроенной функцией round(). С одним аргументом она округляет до целого, с двумя — до нужного числа знаков после точки:

print(round(3.7))
print(round(44.45 - 44, 2))

Результат

4
0.45

У round() есть особенность: ровную половину она округляет к ближайшему чётному. round(0.5) — это 0, а round(1.5) — это 2. Так сделано, чтобы при округлении длинных рядов чисел не копился перекос в одну сторону.

2.12

Дробная часть

Дано положительное действительное число. Выведите его дробную часть.

Формат входных данных
Положительное действительное число.
Формат выходных данных
Дробная часть числа.
Ввод 1
44.45
Вывод 1
0.45
Ввод 2
2.5
Вывод 2
0.5
Подсказка 1

Дробная часть — это само число минус его целая часть.

Подсказка 2

Целую часть у положительного числа даёт int(), а хвост из лишних знаков убирает round().

Разбор
  1. Читаем число как float.
  2. Вычитаем из него int(x) — то, что останется, и есть дробная часть.
  3. Округляем результат до двух знаков: без этого вместо 0.45 напечаталось бы 0.45000000000000284.
Решение
x = float(input())

# int() отбрасывает дробную часть, вычитание оставляет только её,
# а round() убирает хвост, который тянется от двоичного хранения
print(round(x - int(x), 2))

Без round() программа напечатала бы 0.45000000000000284 — верное число с длинным хвостом. Округление до двух знаков — это то, чего просит пример в условии; если бы понадобились все знаки, round() пришлось бы убрать.

2.13

Первая цифра после точки

Дано положительное действительное число. Выведите его первую цифру после десятичной точки.

Формат входных данных
Положительное действительное число.
Формат выходных данных
Одна цифра.
Ввод 1
3384390.4339
Вывод 1
4
Ввод 2
1.5
Вывод 2
5
Подсказка 1

Умножьте число на 10 — нужная цифра станет последней в целой части.

Подсказка 2

Дальше — приёмы первого занятия: отбросить дробь и взять остаток от деления на 10.

Разбор
  1. x * 10 сдвигает точку на разряд вправо: 1.5 превращается в 15.0.
  2. int() отбрасывает всё после точки — остаётся целое 15.
  3. % 10 берёт последнюю цифру: 5.
Решение
x = float(input())

# сдвигаем точку на разряд, отбрасываем остаток дроби и берём последнюю цифру
print(int(x * 10) % 10)

Три знакомых действия подряд — вот и весь ответ. Здесь особенно видно, зачем нужен int(): у дробного числа остаток % тоже дробный, и без него ответом было бы 5.0, а то и 4.999999.

Итог занятия

Главное со второго занятия. Развилка задаётся if, elif, else, а границы её блоков — отступом в четыре пробела. Сравнения складываются в цепочку: -1 <= x <= 17. Связки and, or, not выполняются именно в этом порядке, и в спорных местах скобки дешевле, чем ошибка. Порядок веток каскада — часть решения: сначала проверяют самое строгое условие, потом всё более общее.