Практические задания по программированию на языке Python
Задачи Python разработчика: обзор и решения
Как реализовать классическую задачу FizzBuzz?
Основное решение с использованием цикла и условных операторов:
n = 15
for i in range(1, n + 1):
if i % 3 == 0 and i % 5 == 0:
print('FizzBuzz')
elif i % 3 == 0:
print('Fizz')
elif i % 5 == 0:
print('Buzz')
else:
print(i)
задачи python разработчика (задачи для python-разработчика)
Этот вариант прост для понимания и работает за линейное время. Порядок проверок важен: сначала проверка на оба условия, иначе числа, кратные 15, будут помечены только как Fizz или Buzz.
Как записать FizzBuzz одной строкой через list comprehension?
result = ['FizzBuzz' if i % 15 == 0 else 'Fizz' if i % 3 == 0 else 'Buzz' if i % 5 == 0 else str(i) for i in range(1, 16)]
print('\n'.join(result))
Подходит для вывода как строки, но менее читаемо.
Как использовать функции высшего порядка?
f = lambda i: 'FizzBuzz' if i % 15 == 0 else 'Fizz' if i % 3 == 0 else 'Buzz' if i % 5 == 0 else str(i)
print('\n'.join(map(f, range(1, 16))))
Выглядит функционально, но сложно для отладки.
Типичная ошибка: неправильный порядок проверок (пропуск проверки на 15). Это приводит к тому, что для 15 выводится 'Fizz' или 'Buzz'. Решение: разместить условие с %15 первым.
Еще одна проблема: забывают преобразовать число в строку при использовании join.
Как проверить, является ли строка палиндромом (с учётом только букв и цифр)?
Основной способ: очистить строку от небуквенно-цифровых символов, привести к нижнему регистру и сравнить с обратной версией.
import re
def is_palindrome(s: str) -> bool:
cleaned = re.sub(r'[^a-zA-Z0-9]', '', s).lower()
return cleaned == cleaned[::-1]
print(is_palindrome('A man, a plan, a canal: Panama')) # True
Этот метод лаконичен и корректен для большинства случаев.
Как обойтись без регулярных выражений?
def is_palindrome_no_re(s: str) -> bool:
cleaned = ''.join(ch for ch in s if ch.isalnum()).lower()
return cleaned == cleaned[::-1]
Такой вариант использует isalnum() и генератор, он не зависит от модуля re.
Как проверить с помощью двух указателей (без создания новой строки)?
def is_palindrome_two_pointers(s: str) -> bool:
left, right = 0, len(s) - 1
while left < right:
while left < right and not s[left].isalnum():
left += 1
while left < right and not s[right].isalnum():
right -= 1
if s[left].lower() != s[right].lower():
return False
left += 1
right -= 1
return True
Экономит память, не создавая промежуточную строку.
Распространенная ошибка: игнорирование регистра или неверная очистка от символов, не являющихся буквами и цифрами. Например, проверка строки 'RaceCar' вернет False, если не привести к нижнему регистру.
Пустая строка или строка только из пробелов считается палиндромом.
Как вычислить факториал числа без использования встроенной библиотеки?
Итеративный способ с циклом for:
def factorial_iterative(n: int) -> int:
if n < 0:
raise ValueError('Факториал не определён для отрицательных чисел')
result = 1
for i in range(2, n + 1):
result *= i
return result
print(factorial_iterative(5)) # 120
Итерация не нагружает стек и работает для больших значений (пока позволяет память).
Как написать рекурсивную версию?
def factorial_recursive(n: int) -> int:
if n < 0:
raise ValueError('Факториал не определён для отрицательных чисел')
if n <= 1:
return 1
return n * factorial_recursive(n - 1)
Просто, но может вызвать RecursionError при n > 1000 (зависит от настроек).
Как использовать functools.reduce?
from functools import reduce
def factorial_reduce(n: int) -> int:
if n < 0:
raise ValueError('Факториал не определён')
return reduce(lambda x, y: x * y, range(1, n + 1), 1)
print(factorial_reduce(6)) # 720
Стиль функционального программирования.
Основная ошибка: не обрабатываются отрицательные числа. Также при рекурсии легко превысить лимит глубины. Решение: использовать итеративный подход или увеличить лимит sys.setrecursionlimit().
Для больших чисел (n > 1000) результат может быть очень большим, но Python справляется с большими целыми числами.
Как прочитать данные из CSV файла и преобразовать их в список словарей?
Использование модуля csv.DictReader:
import csv
with open('data.csv', newline='', encoding='utf-8') as f:
reader = csv.DictReader(f)
data = [row for row in reader]
print(data[:2])
Возвращает список словарей, где ключи взяты из первой строки файла.
Как прочитать с помощью csv.reader без заголовков?
with open('data.csv', newline='') as f:
reader = csv.reader(f)
header = next(reader) # первая строка
data = [dict(zip(header, row)) for row in reader]
Позволяет задать свои ключи.
Как использовать pandas для более сложных операций?
import pandas as pd
df = pd.read_csv('data.csv')
data = df.to_dict('records')
Pandas предоставляет множество возможностей фильтрации и анализа, но требует установки внешней библиотеки.
Проблемы с кодировкой: файл может быть в cp1251 или другой. Решение: указать encoding='cp1251' при открытии.
Пропущенные значения и кавычки: csv.DictReader автоматически обрабатывает кавычки, но если в данных есть запятые внутри полей, нужно убедиться, что файл правильно экранирован.
Пустые строки в конце файла: можно добавить проверку на пустую строку при фильтрации.
Расширенные примеры и нестандартные решения
Генератор чисел Фибоначчи с кешированием (декоратор lru_cache)
Использование декоратора functools.lru_cache позволяет избежать повторных вычислений при рекурсивном подходе.
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n: int) -> int:
if n < 2:
return n
return fib(n-1) + fib(n-2)
# вывод первых 20 чисел
[fib(i) for i in range(20)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]
Декоратор автоматически запоминает результаты для уже вычисленных аргументов, что превращает экспоненциальную рекурсию в линейную.
Чтение большого текстового файла по частям (генератор с yield)
Для обработки файлов, не помещающихся в память, используют чтение фиксированными блоками.
def read_in_chunks(file_path: str, chunk_size: int = 1024):
with open(file_path, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
# пример использования
for chunk in read_in_chunks('large_file.bin', 4096):
process(chunk) # вызов пользовательской функции
Генератор не загружает весь файл в память, отдавая данные порциями.
Параллельная обработка списка с помощью multiprocessing.Pool
Ускорение вычислений за счет задействования всех ядер процессора.
from multiprocessing import Pool
import math
def is_prime(n: int) -> bool:
if n < 2:
return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True
if __name__ == '__main__':
numbers = list(range(1, 10001))
with Pool(processes=4) as pool:
results = pool.map(is_prime, numbers)
print(sum(results), 'чисел являются простыми')
1229 чисел являются простыми # результат может незначительно отличаться
Pool.map распределяет элементы входного списка между рабочими процессами и собирает результаты в исходном порядке.
Создание декоратора для замера времени выполнения функции
Полезно для профилирования кода.
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f'{func.__name__} выполнена за {end - start:.6f} сек')
return result
return wrapper
@timer
def slow_sum(n: int) -> int:
return sum(range(n))
slow_sum(10_000_000)
slow_sum выполнена за 0.382934 сек 30000000
Декоратор timer позволяет легко добавлять хронометраж к любой функции без изменения её кода.