← Назад к курсу

Ответы на вопросы по Python

1. Что такое генераторы в Python и чем они отличаются от списков?

Генераторы — это функции, которые возвращают итератор и позволяют лениво (по требованию) генерировать значения с помощью ключевого слова yield. В отличие от списков, они не хранят все элементы в памяти, а вычисляют их по мере необходимости.

Отличия от списков:

  • Память: Генераторы экономят память, так как элементы генерируются «на лету».
  • Скорость: Генераторы быстрее для больших данных.
  • Однопроходность: Генераторы обычно используются один раз (после итерации элементы «заканчиваются»).
  • Доступ: В списках возможен произвольный доступ по индексу, в генераторах — только последовательный.

Пример:

# Генератор
def square_numbers(n):
    for i in range(n):
        yield i ** 2

gen = square_numbers(5)
print(list(gen))  # [0, 1, 4, 9, 16]

# Список
squares_list = [i ** 2 for i in range(5)]  # [0, 1, 4, 9, 16]

2. Объясните разницу между списком, кортежем и множеством в Python.

  • Список (list): Упорядоченная, изменяемая коллекция. Допускает дубликаты.
    lst = [1, 2, 3, 3]
    lst.append(4)  # [1, 2, 3, 3, 4]
    
  • Кортеж (tuple): Упорядоченная, неизменяемая коллекция. Допускает дубликаты. Быстрее списков.
    tup = (1, 2, 3, 3)
    # tup[0] = 5  # Ошибка: нельзя изменить
    
  • Множество (set): Неупорядоченная коллекция уникальных элементов. Изменяемое (кроме frozenset). Поддерживает операции объединения, пересечения и т.д.
    s = {1, 2, 3, 3}  # {1, 2, 3}
    s.add(4)  # {1, 2, 3, 4}
    

3. Что такое декораторы в Python и приведите пример их использования.

Декораторы — это функции, которые изменяют поведение других функций или методов, не изменяя их код. Они принимают функцию как аргумент и возвращают новую функцию.

Пример: Декоратор для измерения времени выполнения.

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"Время выполнения: {time.time() - start:.2f} сек")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(2)

slow_function()  # Вывод: Время выполнения: 2.00 сек

4. Объясните, как работает механизм наследования в Python и что такое множественное наследование.

Наследование позволяет классу (дочернему) наследовать атрибуты и методы другого класса (родительского).

Множественное наследование — когда класс наследуется от нескольких родительских классов. Порядок наследования определяется MRO (Method Resolution Order).

Пример:

class A:
    def greet(self):
        print("Привет от A")

class B:
    def greet(self):
        print("Привет от B")

class C(A, B):
    pass

obj = C()
obj.greet()  # Привет от A (сработает метод из A, так как он первый в MRO)
print(C.__mro__)  # Порядок разрешения методов: C -> A -> B -> object

5. Что такое контекстные менеджеры в Python и как создать свой собственный?

Контекстные менеджеры позволяют управлять ресурсами (например, файлами) с помощью конструкции with. Они автоматически вызывают методы __enter__() (при входе) и __exit__() (при выходе).

Создание своего контекстного менеджера:

  1. Через класс:
    class MyContextManager:
        def __enter__(self):
            print("Вход в контекст")
            return self
        
        def __exit__(self, exc_type, exc_val, exc_tb):
            print("Выход из контекста")
    
    with MyContextManager() as cm:
        print("Внутри контекста")
    
  2. Через contextlib:
    from contextlib import contextmanager
    
    @contextmanager
    def my_context():
        print("Вход")
        yield
        print("Выход")
    
    with my_context():
        print("Внутри")
    

6. Объясните разницу между модулем и пакетом в Python.

  • Модуль: Файл с расширением .py, содержащий код (функции, классы, переменные).
    # module.py
    def hello():
        print("Hello")
    
  • Пакет: Каталог, содержащий модули и файл __init__.py (может быть пустым). Позволяет организовывать модули в пространства имён.
    mypackage/
        __init__.py
        module1.py
        module2.py
    

7. Что такое GIL в Python и как он влияет на многопоточность?

GIL (Global Interpreter Lock) — глобальная блокировка интерпретатора, которая позволяет выполняться только одному потоку Python за раз (даже на многопроцессорных системах).

Влияние:

  • Многопоточность в Python неэффективна для CPU-задач (например, вычислений), так как потоки выполняются последовательно.
  • GIL не мешает I/O-задачам (сеть, файлы), так как во время ожидания освобождается.

Решение для CPU-задач: Использовать многопроцессорность (multiprocessing) или асинхронность.


8. Как работает сборщик мусора в Python и что такое циклические ссылки?

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

Циклические ссылки возникают, когда объекты ссылаются друг на друга, образуя цикл. Пример:

class Node:
    def __init__(self):
        self.ref = None

a = Node()
b = Node()
a.ref = b
b.ref = a  # Циклическая ссылка

В этом случае счётчик ссылок никогда не станет нулевым. Для решения используется циклический сборщик мусора (GC), который обнаруживает и удаляет такие циклы.


9. Объясните разницу между методами экземпляра, класса и статическими методами в Python.

  • Метод экземпляра: Принимает self (экземпляр класса). Работает с данными экземпляра.
    class MyClass:
        def instance_method(self):
            return f"Вызван из {self}"
    
  • Метод класса: Принимает cls (класс). Определяется декоратором @classmethod. Используется для создания альтернативных конструкторов.
    class MyClass:
        @classmethod
        def class_method(cls):
            return f"Вызван из {cls}"
    
  • Статический метод: Не принимает ни self, ни cls. Определяется декоратором @staticmethod. Это обычная функция внутри класса.
    class MyClass:
        @staticmethod
        def static_method():
            return "Простая функция"
    

10. Что такое асинхронное программирование в Python и как работают async и await?

Асинхронное программирование позволяет выполнять I/O-задачи (сеть, файлы) без блокировки потока. Использует однопоточную модель с кооперативной многозадачностью.

Ключевые понятия:

  • async: Определяет асинхронную функцию (корутину).
  • await: Приостанавливает выполнение корутины до завершения другой асинхронной операции.

Пример:

import asyncio

async def fetch_data():
    await asyncio.sleep(2)  # Имитация I/O-операции
    return "Данные"

async def main():
    result = await fetch_data()
    print(result)  # "Данные"

asyncio.run(main())

Асинхронный код выполняется в цикле событий (event loop), который переключается между корутинами при встрече await.