Задачи по Python для начинающих: 10 задач с решениями и разбором

Ноутбук с размытым кодом на экране, рядом тетрадь с решениями задач от руки Изучение

Здесь собраны задачи по Python для начинающих программистов, которые уже знают синтаксис print, if и for: десять задач на числа, условия, циклы, строки, списки и словари. У каждой задачи есть условие, решение, вывод программы и разбор того, где такое решение обычно ломается.

Код проверен на Python 3.14.3; в 3.7 и младше нет math.isqrt. Решения задач 2-8 записаны функциями. Функцию можно вызвать с разными данными и проверить через assert, а ввод с клавиатуры добавляется одним вызовом input(). Сначала напишите своё решение и только потом сверяйтесь с кодом.

Задачи на числа и типы данных

Задача 1. Цена из input()

Напишите программу, которая получает цену товара строкой, как её возвращает input(), и печатает стоимость трёх штук.

input() всегда возвращает строку, даже если пользователь ввёл одни цифры. Умножение строки на число Python ошибкой не считает: строка просто повторяется.

price = "250"          # так приходит любое значение из input()
print(price * 3)
try:
    print(price + 3)
except TypeError as error:
    print("TypeError:", error)
print(int(price) * 3)
250250250
TypeError: can only concatenate str (not "int") to str
750

"250" * 3 склеивает три копии строки, а "250" + 3 падает с TypeError: значения типов str и int сложить нельзя. В рабочем коде строку переводят в int сразу при вводе: price = int(input("Введите цену: ")). Цену с копейками разбирают через float, потому что int("249.90") упадёт с ValueError: invalid literal for int() with base 10: '249.90'. Саму сумму надёжнее считать в копейках целыми числами: 249,90 ₽ хранят как 24990 типа int. Переводить в копейки нужно через round(), а не через int():

for s in ("19.99", "0.29"):
    print(s, int(float(s) * 100), round(float(s) * 100))
19.99 1998 1999
0.29 28 29

float("19.99") * 100 даёт число чуть меньше 1999, и int() просто отрезает дробную часть, теряя копейку. round() возвращает ближайшее целое, и копейки сходятся.

Задача 2. Поделить счёт на компанию

Сумма счёта в кафе делится поровну. Каждый платит целое число рублей, доля округляется вверх. Напишите функцию, которая принимает сумму и количество человек и возвращает долю одного и общую переплату.

def split_bill(total, people):
    share, rest = divmod(total, people)
    if rest:
        share += 1
    return share, share * people - total

print(split_bill(2350, 4), split_bill(2346, 4))
print(round(2350 / 4), round(2346 / 4))
(588, 2) (587, 2)
588 586

divmod() за один вызов отдаёт частное и остаток от деления. Если остаток не равен нулю, доля растёт на рубль. 2350 ₽ на четверых дают 587,5 ₽, в результате каждый платит 588, переплата 2 ₽.

Пара 588 586 в выводе объясняет, почему здесь не годится round(). Половину он округляет к чётному числу: 587,5 стало 588, а 586,5 стало 586. С round() компания из второго примера заплатила бы 4 × 586 = 2344 ₽ и недодала 2 рубля. Для денег берите явное правило: вверх через остаток, как в решении.

Монеты и купюры на столе рядом с ноутбуком и чашкой кофе

Задача 3. Секунды в часы и минуты

Время задано в секундах. Функция получает целое число секунд и возвращает строку вида 2:03:04.

def format_duration(seconds):
    minutes, sec = divmod(seconds, 60)
    hours, minutes = divmod(minutes, 60)
    return f"{hours}:{minutes:02}:{sec:02}"

for s in (59, 3600, 7384, 90061):
    print(s, "->", format_duration(s))
59 -> 0:00:59
3600 -> 1:00:00
7384 -> 2:03:04
90061 -> 25:01:01

Первый divmod делит секунды на минуты и остаток, второй делит минуты на часы. Формат :02 дополняет значение нулём до двух цифр, поэтому 3 минуты печатаются как 03. Часы не ограничены сутками: 90061 секунда дала 25 часов. Если нужны дни, добавьте третий divmod(hours, 24).

Задача 4. Сколько дней в году

Функция принимает номер года и возвращает 365 или 366. Год високосный, если делится на 4, кроме кратных 100, которые не делятся на 400.

def days_in_year(year):
    if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):
        return 366
    return 365

for y in (2024, 2026, 1900, 2000):
    print(y, days_in_year(y))
2024 366
2026 365
1900 365
2000 366

Год 1900 ловит типичную ошибку: если проверить только year % 4 == 0, функция насчитает в нём 366 дней. Готовое правило есть в стандартной библиотеке, это calendar.isleap(year), но в учебной задаче условие пишут сами.

Настенный календарь и ноутбук с размытым кодом на рабочем столе

Задачи на циклы и условные операторы

Задача 5. Простое ли число

Функция is_prime(n) возвращает True, если целое число n простое, и False в противном случае.

from math import isqrt

def is_prime(n):
    if n < 2:
        return False
    for d in range(2, isqrt(n) + 1):
        if n % d == 0:
            return False
    return True

print([n for n in range(20) if is_prime(n)])
print(is_prime(1_000_003), isqrt(1_000_003) - 1)
[2, 3, 5, 7, 11, 13, 17, 19]
True 999

Первая строка вывода показывает список простых чисел до 20. Делители ищем только до корня из n. Если у числа есть делитель больше корня, то парный ему делитель меньше корня, и цикл уже нашёл бы его. Функция isqrt из модуля math возвращает целую часть корня без перехода к float. Для 1 000 003 цикл делает 999 проверок (второе число вывода), перебор всех делителей до n — 1 сделал бы 1 000 001.

Первая проверка n < 2 нужна для 0 и 1. Без неё is_prime(1) вернёт True: range(2, 2) пустой, цикл не выполнится ни разу.

Студент с карандашом в руке думает над задачей за столом с ноутбуком

Задача 6. Компьютер угадывает число

Пользователь загадал число от 1 до 100. Программа называет вариант, слышит «больше» или «меньше» и на каждой итерации отбрасывает одну из двух половин диапазона. Функция возвращает количество попыток, а для значения вне диапазона None.

def guess(secret, low=1, high=100):
    attempts = 0
    while low <= high:
        attempts += 1
        middle = (low + high) // 2
        if middle == secret:
            return attempts
        if middle < secret:
            low = middle + 1
        else:
            high = middle - 1
    return None

print(guess(37), max(guess(n) for n in range(1, 101)), guess(150))
3 7 None

Это бинарный поиск. Для 37 программа назвала 50, потом 25, потом 37. Функция max() получает выражение-генератор по всем ста вариантам: максимальное количество попыток равно 7, потому что за 6 попыток так различают не больше 63 значений (2⁶ — 1). Условие low <= high в заголовке останавливает цикл, когда диапазон опустел, и защищает от числа вне диапазона. Без него guess(150) зациклится: low уйдёт за high, а middle останется равным 100. Как ещё получают вечный while, разобрано в статье о циклах с условием в Python. Границы low и high заданы значениями параметров по умолчанию, поэтому диапазон можно поменять при вызове.

Руки на клавиатуре ноутбука, на экране размытый код с циклом

Задача 7. Вычисление факториала

Функция считает n! = 1 · 2 · … · n. По определению 0! = 1.

import math

def fact(n):
    result = 1
    for k in range(2, n + 1):
        result *= k
    return result

print(fact(0), fact(5), fact(20), fact(20) == math.factorial(20))
try:
    math.factorial(-1)
except ValueError as error:
    print("ValueError:", error)
1 120 2432902008176640000 True
ValueError: factorial() not defined for negative values

result начинается с 1, поэтому 0! и 1! получаются без отдельной проверки: цикл для них не выполняется. Целые числа в Python не переполняются. 21! = 51 090 942 171 709 440 000 больше предела 64-битного целого со знаком (9 223 372 036 854 775 807), а Python печатает такие числа целиком. fact(-1) молча вернёт 1, а math.factorial бросает ValueError. Чтобы своя функция вела себя так же, добавьте первой строкой if n < 0: raise ValueError("n должно быть неотрицательным").

Задачи на работу со строками

Задача 8. Проверка на палиндром

Функция проверяет, читается ли фраза одинаково с обеих сторон, если не учитывать регистр, пробелы и знаки препинания.

def is_palindrome(s):
    letters = [ch for ch in s.casefold() if ch.isalnum()]
    return letters == letters[::-1]

phrase = "А роза упала на лапу Азора"
print(phrase == phrase[::-1])
print(is_palindrome(phrase), is_palindrome("Топот"), is_palindrome("Python"), is_palindrome(""))
False
True True False True

Прямое сравнение строки с перевёрнутой даёт False: заглавная «А» не равна строчной «а», а пробелы стоят в других местах. str.casefold() приводит текст к нижнему регистру, генератор списка отбрасывает символы, для которых isalnum() возвращает False, то есть всё, кроме букв и цифр, а срез [::-1] переворачивает список. Пустая строка считается палиндромом: пустой список равен перевёрнутому. Если в вашем условии это не так, добавьте сразу после строки с letters проверку if not letters: return False.

Ноутбук на глянцевом столе и его зеркальное отражение

Задачи на списки и словари

Задача 9. Самые частые слова

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

from collections import Counter

sentence = "Кот ест, кот спит. Пёс ест и кот ест"
print(sentence.lower().split()[:2])

counts = {}
for word in sentence.lower().split():
    word = word.strip(".,!?")
    counts[word] = counts.get(word, 0) + 1

top = sorted(counts.items(), key=lambda item: item[1], reverse=True)
print(top[:3])
print(Counter(counts).most_common(3))
['кот', 'ест,']
[('кот', 3), ('ест', 3), ('спит', 1)]
[('кот', 3), ('ест', 3), ('спит', 1)]

Решение держится на трёх методах строк: lower(), split() и strip(). split() без аргументов режет строку по пробелам, но запятая остаётся приклеенной к слову, это видно в начале вывода. strip(".,!?") снимает знаки по краям, а counts.get(word, 0) даёт 0 для слова, которого ещё нет в словаре. Сортируем по количеству вхождений. У «кот» и «ест» по три вхождения, и они стоят в порядке первого появления: sorted() устойчив и не переставляет равные элементы. Counter.most_common даёт тот же список одной строкой. Ещё одна задача на строки и списки с разбором трёх ошибок есть в статье про «Фруктовый салат» на Python.

Листы бумаги с выделенными маркером словами рядом с ноутбуком

Задача 10. Убрать повторы из списка

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

cities = ["Казань", "Омск", "Казань", "Тула", "Омск"]
print(list(dict.fromkeys(cities)), len(set(cities)))
['Казань', 'Омск', 'Тула'] 3

list(set(cities)) тоже убирает повторы, но порядок элементов у множества не гарантирован. Для текстовых значений он меняется от запуска к запуску: хеши строк при каждом старте интерпретатора солятся случайным значением. Мы запустили эту строку с тремя разными PYTHONHASHSEED и получили два порядка: ['Казань', 'Тула', 'Омск'] и ['Омск', 'Казань', 'Тула']. Словарь хранит ключи в порядке вставки начиная с Python 3.7, поэтому dict.fromkeys сохраняет порядок исходного списка. Множество используйте, чтобы проверить вхождение через in или посчитать уникальные значения: len(set(cities)) возвращает их количество.

Ряд одинаковых деревянных кубиков, лишние повторы отодвинуты в сторону

Как проверить своё решение

Решения выше удобно проверять через assert: он молчит, если условие верно, и бросает AssertionError, если нет. Допишите проверки в конец файла с функциями:

assert split_bill(2350, 4) == (588, 2)
assert format_duration(7384) == "2:03:04"
assert days_in_year(1900) == 365
assert is_prime(2) and not is_prime(1)
assert is_palindrome("Топот")
print("Все проверки пройдены")

def days_naive(year):
    return 366 if year % 4 == 0 else 365

try:
    assert days_naive(1900) == 365, "1900 не високосный"
except AssertionError as error:
    print("AssertionError:", error)
Все проверки пройдены
AssertionError: 1900 не високосный

Вторая часть показывает, как проверка ловит ошибку: наивная функция из задачи 4 считает 1900 год високосным. Обёртка try здесь только для того, чтобы вывести сообщение; в своём файле оставьте голый assert, и программа остановится с traceback на первой неверной проверке. Запуск через python -O выключает assert, поэтому проверки гоняйте обычным python. В каждую проверку берите граничный случай: 0, 1, пустую строку, год 1900.

Экран ноутбука с размытым выводом тестов в терминале, рядом чашка

Частые вопросы

Как добавить в решение ввод с клавиатуры?

Функцию оставьте без изменений, а ввод напишите отдельно перед вызовом: total = int(input("Сумма счёта: ")), затем print(split_bill(total, 4)). Так функция по-прежнему проверяется через assert, а пользователь вводит данные сам.

С каких задач начать?

С задач 1-4: их решения обходятся без циклов. Если решение не придумывается сразу, выпишите на бумаге два-три входа и ожидаемый ответ, как в выводе каждой задачи, и только потом пишите код.

Нужна ли математика для задач по Python?

Для этих десяти задач хватает деления с остатком и понятия квадратного корня.

Код не запускается, что проверить?

Сначала версию языка: python --version. В Python 3.7 и младше math.isqrt нет. Если ошибка начинается с ModuleNotFoundError, причины и исправления разобраны в статье про ошибку No module named. Не давайте файлу с решением имя модуля стандартной библиотеки: свой random.py заслонит настоящий random, и random.randint упадёт с AttributeError: module 'random' has no attribute 'randint' (consider renaming ....

Оцените статью
bestprogrammer.ru
Добавить комментарий