""" Урок 57. Практика 14 — весь код примеров одним файлом. Источник: subjects/python-fundamentals/course/lessons/57-practice-14/examples.html Файл собран автоматически (tools/build_lesson_examples.py): правьте страницу урока. Запуск: python lesson-57.py """ # ==================================================================== # Пример 1. custom_range: собственный генератор вместо range() # ==================================================================== def custom_range(start, stop, step=1): while start < stop: yield start start += step for num in custom_range(2, 10, 2): print(num) # 2 # 4 # 6 # 8 print(type(custom_range(0, 3))) # — вызов не выполняет тело функции # ==================================================================== # Пример 2. Ловушка границы: < против <= # ==================================================================== def custom_range_bad(start, stop, step=1): while start <= stop: # лишнее равенство yield start start += step print("bad:", list(custom_range_bad(1, 5))) # [1, 2, 3, 4, 5] — включает 5 print("good:", list(custom_range(1, 5))) # [1, 2, 3, 4] — не включает 5 print("range():", list(range(1, 5))) # [1, 2, 3, 4] — эталон поведения # ==================================================================== # Пример 3. Случайные даты: словарь дней месяца и високосный год # ==================================================================== import random random.seed(42) # для воспроизводимого вывода в примере def is_leap(year: int) -> bool: return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0) def random_dates(year: int): days_in_month = { 1: 31, 2: 29 if is_leap(year) else 28, 3: 31, 4: 30, 5: 31, 6: 30, 7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31 } while True: month = random.randint(1, 12) day = random.randint(1, days_in_month[month]) yield f"{year}-{month:02d}-{day:02d}" print(is_leap(2024), is_leap(2023), is_leap(1900), is_leap(2000)) # True False False True — 1900 делится на 100, но не на 400: не високосный date_gen = random_dates(2025) for _ in range(5): print(next(date_gen)) # 2025-11-04 # 2025-01-24 # 2025-05-08 # 2025-04-05 # 2025-12-04 # ==================================================================== # Пример 4. Случайные имена: комбинация двух списков через random.choice # ==================================================================== from random import choice def random_names(): first_names = ["Alice", "Bob", "Charlie", "David", "Emma"] last_names = ["Smith", "Johnson", "Brown", "Williams", "Jones"] while True: yield f"{choice(first_names)} {choice(last_names)}" name_gen = random_names() for _ in range(5): print(next(name_gen)) # Emma Smith # Emma Williams # Alice Smith # Alice Johnson # Bob Jones # ==================================================================== # Пример 5. combine_generators: объединить несколько генераторов в один поток # ==================================================================== def combine_generators(*generators): while True: yield tuple(next(gen) for gen in generators) name_gen2 = random_names() date_gen2 = random_dates(2025) data_gen = combine_generators(name_gen2, date_gen2) for _ in range(5): print(next(data_gen)) # ('Emma Smith', '2025-09-07') # ('Emma Williams', '2025-04-15') # ('Emma Brown', '2025-01-25') # ('Bob Williams', '2025-06-09') # ('Bob Johnson', '2025-06-04') # ==================================================================== # Пример 6. Циклическое распределение через itertools.cycle # ==================================================================== import itertools employees = ["Alice", "Bob", "Charlie"] tasks = ["Отчёт", "Письмо", "Презентация", "Звонок", "Обзор кода"] employee_cycle = itertools.cycle(employees) for task in tasks: employee = next(employee_cycle) print(f"{employee} выполняет: {task}") # Alice выполняет: Отчёт # Bob выполняет: Письмо # Charlie выполняет: Презентация # Alice выполняет: Звонок — цикл начался заново # Bob выполняет: Обзор кода # ==================================================================== # Пример 7. Запись задач в файл: add_tasks() # ==================================================================== def add_tasks(): while True: task = input("Введите задачу (или 'exit' для выхода): ").strip() if task.lower() == "exit": break with open("tasks.txt", "a", encoding="utf-8") as file: file.write(task + "\n") print("Задача добавлена!") add_tasks() # при вводе "Отчёт", "Письмо", "exit" — в консоли: # Задача добавлена! # Задача добавлена! with open("tasks.txt", "r", encoding="utf-8") as file: print(file.read()) # Отчёт # Письмо # ==================================================================== # Пример 8. Чтение и распределение задач: task_assigner() # ==================================================================== def task_assigner(employees, filename="tasks.txt"): employee_cycle = itertools.cycle(employees) attempts = 0 retries = 5 while attempts < retries: try: with open(filename, "r", encoding="utf-8") as file: while True: task = file.readline().strip() if task: yield next(employee_cycle), task else: return # для этого примера: выходим, а не ждём новых строк except FileNotFoundError: attempts += 1 print(f"Файл не найден, попытка {attempts}/{retries}...") employees = ["Alice", "Bob", "Charlie"] for employee, task in itertools.islice(task_assigner(employees), 5): print(f"{employee} выполняет: {task}") # Alice выполняет: Отчёт # Bob выполняет: Письмо # (запросили 5 — получили только 2: в файле tasks.txt всего 2 строки) # ==================================================================== # Пример 9. Почему у бесконечного генератора обязательно должен быть внешний предел # ==================================================================== def counting_forever(): n = 0 while True: n += 1 yield n infinite = counting_forever() first_five = [next(infinite) for _ in range(5)] print(first_five) # [1, 2, 3, 4, 5] — ограничили range(5), а не сам генератор print(list(itertools.islice(counting_forever(), 3))) # [1, 2, 3]