Skip to content

Лайвкодинг

1. Напишите функцию, которая возвращает n-ное число Фибоначчи

Задача
    def fibonacci(n):
     """
     (Последовательность Фибоначчи: 0, 1, 1, 2, 3, 5, 8, 13, ...)
     """
        # Ваше решение здесь
        pass

    # Тесты
    print(fibonacci(0))  # 0
    print(fibonacci(1))  # 1
    print(fibonacci(5))  # 5
    print(fibonacci(7))  # 13
Спойлеры к решению
1. Последовательность Фибоначчи - каждое следующее число = сумма двух предыдущих
2. Есть несколько способов решения:
    - с помощью рекурсии
    - с помощью цикла for
Решение 1. Рекурсивное решение (неэффективное)
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)
2. Итеративное решение (оптимальное)

def fibonacci(n):
    if n <= 1:
        return n

    a, b = 0, 1
    for i in range(2, n + 1):
        a, b = b, a + b
    return b
3. Генератор чисел Фибоначчи

def fibonacci_generator(limit):
    a, b = 0, 1
    while a <= limit:
        yield a
        a, b = b, a + b

# Использование
for num in fibonacci_generator(20):
    print(num)  # 0, 1, 1, 2, 3, 5, 8, 13

2. Напишите функцию, которая возвращает следующий день, относительно переданной даты

Задача
    def get_next_day(date_string):
        """
        Args:
           date_string (str): Дата в формате 'YYYY-MM-DD'

        Returns:
           str: Следующий день в формате 'YYYY-MM-DD'
        """

        # Ваше решение здесь
        pass

    # Тесты
    print(get_next_day('2024-01-31'))  # 2024-02-01
    print(get_next_day('2024-02-28'))  # 2024-02-29
    print(get_next_day('2023-02-28'))  # 2023-03-01
Спойлеры к решению
1. Применить datetime, strptime, strftime
Решение
from datetime import datetime, timedelta

def get_next_day(date_string):
    """
    Возвращает следующий день относительно переданной даты.

    Args:
        date_string (str): Дата в формате 'YYYY-MM-DD'

    Returns:
        str: Следующий день в формате 'YYYY-MM-DD'
    """
    current_date = datetime.strptime(date_string, '%Y-%m-%d')
    next_date = current_date + timedelta(days=1)
    return next_date.strftime('%Y-%m-%d')

3. Написать генератор для пагинации по списку

Задача
    def paginate(items: list, page_size:int):
        """
        Генератор для пагинации по списку.

        Args:
        items: список элементов
        page_size: размер страницы

        Yields:
            списки элементов размером page_size
        """

        # Ваше решение здесь
        pass

    # Тесты
    items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    paginator = paginate(items, 3)
    assert next(paginator) == [1, 2, 3]
    assert next(paginator) == [4, 5, 6]
    assert next(paginator) == [7, 8, 9]
    assert next(paginator) == [10]

    # Проверяем StopIteration
    try:
        next(paginator)
        assert False, "Ожидалось StopIteration"
    except StopIteration:
        pass  # Ожидаемое поведение

    for page in paginator:
        print(page)  # [1, 2, 3], [4, 5, 6], [7, 8, 9], [10]
Спойлеры к решению
1. Нужно разбить список на части по page_size элементов
2. Используйте срезы списка: items[start:end]
3. Шаг между страницами = page_size
Решение Вариант 1: Классический с range (самый популярный)

def paginate(items, page_size):
    for i in range(0, len(items), page_size):
        yield items[i:i + page_size]
Вариант 2: С использованием while цикла
def paginate(items, page_size):
    i = 0
    while i < len(items):
        yield items[i:i + page_size]
        i += page_size
Вариант 3: С итератором и islice

from itertools import islice

def paginate(items, page_size):
    iterator = iter(items)
    while True:
        chunk = list(islice(iterator, page_size))
        if not chunk:
            return
        yield chunk
Вариант 4: Рекурсивный подход

def paginate(items, page_size):
    if not items:
        return
    yield items[:page_size]
    yield from paginate(items[page_size:], page_size)
Вариант 5: С использованием enumerate

def paginate(items, page_size):
    for i, item in enumerate(items):
        if i % page_size == 0:
            yield items[i:i + page_size]
Вариант 6: С группировкой по индексу

def paginate(items, page_size):
    return (items[i:i + page_size] for i in range(0, len(items), page_size))
Вариант 7: С проверкой границ

def paginate(items, page_size):
    if page_size <= 0:
        raise ValueError("page_size должен быть положительным")

    page = []
    for item in items:
        page.append(item)
        if len(page) == page_size:
            yield page
            page = []

    if page:  # Последняя неполная страница
        yield page

4. Написать генератор, который сделает из вложенного словаря плоский

Задача
    def flatten_dict(nested_dict: dict, parent_key: str = '', separator: str = '.'):
        """
        Генератор, который преобразует вложенный словарь в плоский.

        Args:
            nested_dict: вложенный словарь
            parent_key: ключ родительского элемента (для рекурсии)
            separator: разделитель между ключами

        Yields:
            кортежи (ключ, значение) для каждого элемента плоского словаря
        """

        # Ваше решение здесь
        pass

    # Тесты
    nested = {
        'a': 1,
        'b': {
            'c': 2,
            'd': {
                'e': 3
            }
        },
        'f': 4
    }

    # Преобразуем в словарь для проверки
    result = dict(flatten_dict(nested))
    expected = {
        'a': 1,
        'b.c': 2,
        'b.d.e': 3,
        'f': 4
    }
    assert result == expected

    # Проверяем работу как генератора
    flat_gen = flatten_dict(nested)
    assert next(flat_gen) == ('a', 1)
    assert next(flat_gen) == ('b.c', 2)

    # Проверяем StopIteration
    try:
        next(flat_gen)
        assert False, "Ожидалось StopIteration"
    except StopIteration:
        pass
Спойлеры к решению
1. Нужно обойти все ключи словаря рекурсивно
2. Если значение является словарем - углубляемся в него
3. Формируем составной ключ через разделитель
4. Используйте yield для возврата пар (ключ, значение)
Решение Вариант 1: Рекурсивный с yield from
def flatten_dict(nested_dict: dict, parent_key: str = '', separator: str = '.'):
    for key, value in nested_dict.items():
        new_key = f"{parent_key}{separator}{key}" if parent_key else key

        if isinstance(value, dict):
            yield from flatten_dict(value, new_key, separator)
        else:
            yield (new_key, value)
Вариант 2: С явным yield для каждого случая
def flatten_dict(nested_dict: dict, parent_key: str = '', separator: str = '.'):
    for key, value in nested_dict.items():
        new_key = f"{parent_key}{separator}{key}" if parent_key else key

        if isinstance(value, dict):
            for flattened in flatten_dict(value, new_key, separator):
                yield flattened
        else:
            yield (new_key, value)
Вариант 3: С обработкой списков и других типов
def flatten_dict(nested_dict: dict, parent_key: str = '', separator: str = '.'):
    for key, value in nested_dict.items():
        new_key = f"{parent_key}{separator}{key}" if parent_key else key

        if isinstance(value, dict):
            yield from flatten_dict(value, new_key, separator)
        elif isinstance(value, (list, tuple)):
            for i, item in enumerate(value):
                list_key = f"{new_key}[{i}]"
                if isinstance(item, dict):
                    yield from flatten_dict(item, list_key, separator)
                else:
                    yield (list_key, item)
        else:
            yield (new_key, value)
Вариант 4: Итеративный с использованием стека
def flatten_dict(nested_dict: dict, separator: str = '.'):
    stack = [(nested_dict, '')]

    while stack:
        current_dict, current_key = stack.pop()

        for key, value in current_dict.items():
            new_key = f"{current_key}{separator}{key}" if current_key else key

            if isinstance(value, dict):
                stack.append((value, new_key))
            else:
                yield (new_key, value)

5. Реализовать класс библиотеки

Задача
    class Library:
        """
        Простой класс для управления библиотекой книг.
        """

        def __init__(self):
            """Инициализация пустой библиотеки."""
            self.books = []

        def add_book(self, title: str, author: str, year: int):
            """
            Добавляет книгу в библиотеку.

            Args:
                title: название книги
                author: автор книги
                year: год издания
            """
            # Ваше решение здесь
            pass

        def find_by_author(self, author: str) -> list:
            """
            Находит книги по автору.

            Args:
                author: автор для поиска

            Returns:
                список найденных книг
            """
            # Ваше решение здесь
            pass

        def get_all_books(self) -> list:
            """
            Возвращает все книги.

            Returns:
                список всех книг
            """
            # Ваше решение здесь
            pass

    # Тесты
    library = Library()

    # Добавление книг
    library.add_book("Война и мир", "Лев Толстой", 1869)
    library.add_book("Анна Каренина", "Лев Толстой", 1877)
    library.add_book("Преступление и наказание", "Федор Достоевский", 1866)

    # Поиск по автору
    tolstoy_books = library.find_by_author("Лев Толстой")
    assert len(tolstoy_books) == 2

    # Получение всех книг
    all_books = library.get_all_books()
    assert len(all_books) == 3

    print("Все тесты пройдены!")
Спойлеры к решению
1. Используйте список для хранения книг
2. Каждая книга - словарь с полями title, author, year
3. Для поиска используйте фильтрацию списка
Решение
class Library:
    def __init__(self):
        self.books = []

    def add_book(self, title: str, author: str, year: int):
        book = {
            'title': title,
            'author': author, 
            'year': year
        }
        self.books.append(book)

    def find_by_author(self, author: str) -> list:
        return [book for book in self.books if book['author'] == author]

    def get_all_books(self) -> list:
        return self.books.copy()

6. Задача (с подвохом). Что выведет этот код?

Задача
def extend_list(val, lst=[]):
    lst.append(val)
    return lst

# Первый вызов: используем список по умолчанию
list1 = extend_list(10)

# Второй вызов: передаём новый пустой список
list2 = extend_list(123, [])

# Третий вызов: снова используем список по умолчанию
list3 = extend_list('a')

print('list1 =', list1)
print('list2 =', list2)
print('list3 =', list3)
Решение Значения по умолчанию для аргументов функции вычисляются один раз в момент определения функции, а не при каждом вызове. Поэтому список lst=[] создается единожды и переиспользуется во всех вызовах, где lst не передается явно.
Первый вызов:
list1 = extend_list(10)

# lst не передан → используется дефолтный список []
# Добавляем 10 → список становится [10]
# list1 = [10]
Второй вызов:
list2 = extend_list(123, [])

# Передаем новый пустой список `[]` 
# Добавляем `123` → список становится `[123]`
# list2 = [123]
Третий вызов:
#  = extend_list('a')

# lst не передан → используется тот же дефолтный список, что и в первом вызове (уже `[10]`)
# Добавляем `'a'` → список становится `[10, 'a']`
# list3 = [10, 'a']**
# Итоговый вывод:
list1 = [10, 'a']
list2 = [123] 
list3 = [10, 'a']
⚠️ **Важно:** `list1` и `list3` ссылаются на **один и тот же** дефолтный список, поэтому изменения сохраняются между вызовами.
Правильный вывод
list1 = [10, 'a']
list2 = [123]
list3 = [10, 'a']

7. Реализовать базовый класс Robot

Задача
class Robot:
    """
    Базовый класс для роботов с базовыми боевыми характеристиками.
    """

    def __init__(self, name):
        """
        Конструктор робота.

        Args:
            name: кличка робота
        """
        self.name = name
        self.health = 100
        self.attack_power = 10

    def attack(self, target):
        """
        Атакует другого робота.

        Args:
            target: объект робота-цели
        """
        # Ваше решение здесь
        pass

    def take_damage(self, amount):
        """
        Обрабатывает полученный урон.

        Args:
            amount: количество единиц урона
        """
        # Ваше решение здесь
        pass

    @property
    def is_alive(self):
        """
        Проверяет, жив ли робот.

        Returns:
            bool: True если здоровье > 0, иначе False
        """
        # Ваше решение здесь
        pass

    def __str__(self):
        """
        Возвращает строковое представление робота.

        Returns:
            str: информация о роботе
        """
        # Ваше решение здесь
        pass

# Тесты
robot1 = Robot("R2-D2")
robot2 = Robot("C-3PO")

print("До атаки:")
print(robot1)
print(robot2)

robot1.attack(robot2)

print("\nПосле атаки:")
print(robot1)
print(robot2)

print(f"\n{robot2.name} жив: {robot2.is_alive}")
Спойлеры к решению
1. Для is_alive используйте property декоратор
2. В attack вызывайте take_damage у цели
3. В take_damage уменьшайте health и проверяйте на смерть
4. В __str__ возвращайте форматированную строку с именем и здоровьем
Решение
class Robot:
    """
    Базовый класс для роботов с базовыми боевыми характеристиками.
    """

    def __init__(self, name):
        self.name = name
        self.health = 100
        self.attack_power = 10

    def attack(self, target):
        print(f"{self.name} атакует {target.name} с силой {self.attack_power}!")
        target.take_damage(self.attack_power)

    def take_damage(self, amount):
        self.health -= amount
        print(f"{self.name} получает {amount} урона. Здоровье: {self.health}")

        if self.health <= 0:
            print(f"💀 {self.name} уничтожен!")

    @property
    def is_alive(self):
        return self.health > 0

    def __str__(self):
        status = "жив" if self.is_alive else "уничтожен"
        return f"Робот {self.name} | Здоровье: {self.health} | Статус: {status}"

# Тесты
robot1 = Robot("R2-D2")
robot2 = Robot("C-3PO")

print("До атаки:")
print(robot1)
print(robot2)

robot1.attack(robot2)

print("\nПосле атаки:")
print(robot1)
print(robot2)

print(f"\n{robot2.name} жив: {robot2.is_alive}")
**Ожидаемый вывод:**
До атаки:
Робот R2-D2 | Здоровье: 100 | Статус: жив
Робот C-3PO | Здоровье: 100 | Статус: жив

R2-D2 атакует C-3PO с силой 10!
C-3PO получает 10 урона. Здоровье: 90

После атаки:
Робот R2-D2 | Здоровье: 100 | Статус: жив
Робот C-3PO | Здоровье: 90 | Статус: жив

C-3PO жив: True

8. Написать запросы Django ORM для моделей City и Person

Задача
# Даны модели:
class City(models.Model):
    name = models.CharField(max_length=100)

class Person(models.Model):
    name = models.CharField(max_length=100)
    city = models.ForeignKey(City, on_delete=models.CASCADE)

# Необходимо выполнить следующие запросы:
# 1. Вывести список людей и городов где они живут
# 2. Вывести всех людей, живущих в городе N
# 3. Вывести 5 городов с наибольшим населением, упорядочив по убыванию
Спойлеры к решению
1. Для связанных моделей используйте select_related() и двойное подчеркивание
2. Для фильтрации используйте filter() с параметрами через двойное подчеркивание
3. Для агрегации используйте annotate() с Count() и order_by()
4. Для ограничения результатов используйте срезы [:N]
Решение
from django.db.models import Count

# 1. Вывести список людей и городов где они живут
people_with_cities = Person.objects.select_related('city').all()
for person in people_with_cities:
    print(f"{person.name} - {person.city.name}")

# Альтернативный вариант
people_data = Person.objects.select_related('city').values_list('name', 'city__name')
for name, city_name in people_data:
    print(f"{name} - {city_name}")

# 2. Вывести всех людей, живущих в городе N
city_name = "Москва"
people_in_city = Person.objects.select_related('city').filter(city__name=city_name)
for person in people_in_city:
    print(f"{person.name} живет в {person.city.name}")

# Если известен ID города
city_id = 1
people_in_city_by_id = Person.objects.filter(city_id=city_id)

# 3. Вывести 5 городов с наибольшим населением, упорядочив по убыванию
top_cities = City.objects.annotate(
    population=Count('person')
).order_by('-population')[:5]

for city in top_cities:
    print(f"{city.name} - {city.population} жителей")

# Альтернативный вариант с values
top_cities_data = City.objects.annotate(
    population=Count('person')
).order_by('-population').values('name', 'population')[:5]

for city in top_cities_data:
    print(f"{city['name']} - {city['population']} жителей")

9. Реализовать запуск миллиона асинхронных задач с ограничением concurrency

Задача
Необходимо запустить 1_000_000 асинхронных задач с ограничением:
не более 5 задач могут выполняться одновременно.

Требования:
1. Создать 1_000_000 задач с уникальными идентификаторами
2. Ограничить одновременное выполнение до 5 задач
3. Каждая задача должна имитировать работу (sleep 1 секунду)
4. Логировать начало и завершение каждой задачи
5. Обеспечить корректное выполнение всех задач без перегрузки системы
Спойлеры к решению
1. Используйте asyncio.Semaphore(5) для ограничения concurrency
2. Применяйте async with semaphore: внутри функции задачи
3. Используйте asyncio.gather для запуска всех задач
4. Для имитации работы используйте asyncio.sleep(1)
5. Убедитесь, что все 1_000_000 задач будут выполнены
Решение
import asyncio

async def limited_task(task_id, semaphore):
    """
    Асинхронная задача с ограничением одновременного выполнения.

    Args:
        task_id: уникальный идентификатор задачи
        semaphore: семафор для контроля количества одновременных задач
    """
    async with semaphore:
        print(f"🚀 Задача {task_id} начата")
        await asyncio.sleep(1)  # Имитация работы (1 секунда)
        print(f"✅ Задача {task_id} завершена")
        return f"Результат задачи {task_id}"

async def main():
    """
    Основная функция для запуска 1_000_000 задач с ограничением concurrency.
    """
    # Создаем семафор, ограничивающий одновременное выполнение до 5 задач
    semaphore = asyncio.Semaphore(5)

    # Создаем 1_000_000 задач с уникальными идентификаторами
    tasks = [limited_task(i, semaphore) for i in range(1_000_000)]

    print(f"🎯 Запуск {len(tasks)} задач с ограничением 5 одновременных выполнений")

    # Запускаем все задачи конкурентно
    results = await asyncio.gather(*tasks, return_exceptions=True)

    # Статистика выполнения
    successful = len([r for r in results if not isinstance(r, Exception)])
    failed = len([r for r in results if isinstance(r, Exception)])

    print(f"📊 Статистика: Успешно {successful}, Ошибок {failed}")

# Запуск асинхронного приложения
if __name__ == "__main__":
    asyncio.run(main())

10. Написать декоратор с параметрами для форматирования текста

Задача
# Написать декоратор formatter, который принимает параметр - тег HTML
# и оборачивает результат функции в соответствующий HTML-тег

# Пример использования:
@formatter('b')
def hello(name: str):
    return f'Hello, {name}'

@formatter('i') 
def goodbye(name: str):
    return f'Goodbye, {name}'

# Ожидаемый результат:
# hello('Bob') → '<b>Hello, Bob</b>'
# goodbye('John') → '<i>Goodbye, John</i>'
Спойлеры к решению
1. Декоратор с параметром должен возвращать функцию-декоратор
2. Внутренняя функция-wrapper должна вызывать оригинальную функцию
3. Результат нужно обернуть в HTML-теги согласно параметру
4. Используйте замыкание для доступа к параметру letter
Решение
def formatter(letter):
    """
    Декоратор с параметром для оборачивания результата в HTML-тег.

    Args:
        letter: символ HTML-тега (например, 'b', 'i', 'p')

    Returns:
        декоратор функции
    """
    def letter_formatter(func):
        def wrapper(*args, **kwargs):
            # Вызываем оригинальную функцию
            result = func(*args, **kwargs)
            # Оборачиваем результат в HTML-теги
            formatted_result = f'<{letter}>{result}</{letter}>'
            return formatted_result
        return wrapper    
    return letter_formatter

# Пример использования
@formatter('b')
def hello(name: str):
    return f'Hello, {name}'

@formatter('i')
def goodbye(name: str):
    return f'Goodbye, {name}'

# Тестирование
assert hello('Bob') == '<b>Hello, Bob</b>'
assert goodbye('John') == '<i>Goodbye, John</i>'

print("✅ Все тесты пройдены!")
print(hello('Bob'))      # <b>Hello, Bob</b>
print(goodbye('John'))   # <i>Goodbye, John</i>