Python | Вопросы собесов
13K subscribers
42 photos
7 videos
1 file
1.49K links
Сайт: https://easyoffer.ru/
Все каналы: t.me/+xGeAw6ckJ4liYzQy

Контакт для рекламы: @easyoffer_adv
Download Telegram
🤔 Что такое паттерн Стратегия (Strategy) ?

Это поведенческий паттерн проектирования, который определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. Паттерн "Стратегия" позволяет изменять алгоритмы независимо от клиентов, которые их используют.

🚩Зачем нужен данный паттерн?

🟠Изоляция алгоритмов
Позволяет инкапсулировать различные алгоритмы и использовать их независимо.
🟠Упрощение кода
Устраняет дублирование кода и упрощает классы, которые используют эти алгоритмы.
🟠Гибкость и расширяемость
Легко добавлять новые алгоритмы или изменять существующие без изменения клиентского кода.

🚩Как работает данный паттерн?

🟠Стратегия (Strategy)
Интерфейс, определяющий общий метод, который должны реализовать все алгоритмы.
🟠Конкретные стратегии (ConcreteStrategy)
Реализации различных алгоритмов, которые реализуют интерфейс стратегии.
🟠Контекст (Context)
Класс, использующий стратегию для выполнения задачи.

from abc import ABC, abstractmethod

# Интерфейс стратегии
class Strategy(ABC):
@abstractmethod
def sort(self, data):
pass

# Конкретные стратегии
class BubbleSortStrategy(Strategy):
def sort(self, data):
print("Sorting using Bubble Sort")
for i in range(len(data)):
for j in range(0, len(data)-i-1):
if data[j] > data[j+1]:
data[j], data[j+1] = data[j+1], data[j]

class QuickSortStrategy(Strategy):
def sort(self, data):
print("Sorting using Quick Sort")
self.quick_sort(data, 0, len(data) - 1)

def quick_sort(self, data, low, high):
if low < high:
pi = self.partition(data, low, high)
self.quick_sort(data, low, pi - 1)
self.quick_sort(data, pi + 1, high)

def partition(self, data, low, high):
pivot = data[high]
i = low - 1
for j in range(low, high):
if data[j] <= pivot:
i = i + 1
data[i], data[j] = data[j], data[i]
data[i + 1], data[high] = data[high], data[i + 1]
return i + 1

# Контекст
class SortingContext:
def __init__(self, strategy: Strategy):
self._strategy = strategy

def set_strategy(self, strategy: Strategy):
self._strategy = strategy

def sort(self, data):
self._strategy.sort(data)

# Клиентский код
data = [5, 2, 9, 1, 5, 6]

context = SortingContext(BubbleSortStrategy())
context.sort(data)
print(data) # [1, 2, 5, 5, 6, 9]

context.set_strategy(QuickSortStrategy())
data = [3, 7, 8, 5, 2, 1, 9, 5, 4]
context.sort(data)
print(data) # [1, 2, 3, 4, 5, 5, 7, 8, 9]


🚩Плюсы и минусы

Изоляция алгоритмов
Алгоритмы инкапсулируются в отдельные классы, что упрощает их замену и добавление.
Упрощение кода
Контекст использует стратегии, избегая громоздких условных операторов.
Гибкость и расширяемость
Легко добавлять новые стратегии без изменения существующего кода.
Усложнение структуры кода

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

🚩Когда использовать данный паттерн?

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

Ставь 👍 и забирай 📚 Базу знаний
👍2
🤔 Разница между компилируемыми и интерпретируемыми языками?

- Компилируемые: исходный код переводится в машинный до выполнения. Пример: C++.
- Интерпретируемые: код исполняется построчно во время выполнения. Пример: Python. Python — интерпретируемый, хотя использует промежуточную байткод-компиляцию.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍2
🤔 Назови основные команды docker?

Docker — это инструмент для создания, развертывания и управления контейнерами. Основные команды позволяют управлять образами, контейнерами, сетями и томами.

🚩Работа с образами (`images`)

Образы — это "шаблоны" для создания контейнеров.
Пример: скачиваем Python-образ
docker pull python:3.11


🚩Работа с контейнерами (`containers`)

Контейнер — это запущенный процесс на основе образа.
Пример: запустить контейнер с Ubuntu и войти в него
docker run -it ubuntu bash


Пример: остановить и удалить контейнер
docker stop my_app
docker rm my_app


🚩Работа с томами (`volumes`)

Том (volume) — это способ хранения данных, которые не пропадут при перезапуске контейнера.
Пример: подключить том к контейнеру
docker run -v my_data:/app/data ubuntu


🚩Работа с сетями (`networks`)

Сети в Docker позволяют контейнерам взаимодействовать друг с другом.
Пример: запустить два контейнера в одной сети
docker network create my_network
docker run -d --network my_network --name app1 ubuntu
docker run -d --network my_network --name app2 ubuntu


🚩5. Docker Compose (`docker-compose.yml`)

Docker Compose позволяет управлять несколькими контейнерами с помощью docker-compose.yml.
Пример docker-compose.yml
version: "3"
services:
app:
image: python:3.11
volumes:
- my_data:/app/data
networks:
- my_network

volumes:
my_data:

networks:
my_network:


Запуск
docker compose up -d


Ставь 👍 и забирай 📚 Базу знаний
👍3
🤔 Название переменных?

Должны начинаться с буквы или подчёркивания, содержать только буквы, цифры и подчёркивания. Не допускается совпадение с ключевыми словами.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍2
🤔 Что такое паттерн Заместитель (Proxy)?

Это структурный шаблон проектирования, который предоставляет объект, управляющий доступом к другому объекту. Этот паттерн создаёт суррогат или заместителя для другого объекта и контролирует доступ к нему.

🚩Зачем нужен паттерн Заместитель

🟠Управление доступом
Когда необходимо контролировать доступ к ресурсу.
🟠Отложенная инициализация
Когда необходимо отложить создание ресурсоёмких объектов до момента их первого использования.
🟠Управление ресурсами
Для управления ресурсами, такими как память или сетевые соединения.
🟠Логирование и кэширование
Для добавления дополнительной функциональности, такой как логирование или кэширование, без изменения кода основного объекта.

🚩Типы заместителей

🟠Управляющий заместитель (Virtual Proxy):
Контролирует доступ к объекту, создавая его по требованию.
🟠Защитный заместитель (Protection Proxy):
Контролирует доступ к объекту, ограничивая права пользователей.
🟠Удалённый заместитель (Remote Proxy)
Управляет доступом к объекту, находящемуся в другом адресном пространстве.
🟠Кэш-прокси (Cache Proxy)
Кэширует результаты запросов к объекту для повышения производительности.

🚩Как используется паттерн Заместитель

Заместитель реализует интерфейс основного объекта и перенаправляет вызовы к реальному объекту, добавляя при этом дополнительную функциональность. В этом примере класс Proxy контролирует доступ к классу RealSubject, добавляя проверку доступа и логирование.
from abc import ABC, abstractmethod

class Subject(ABC):
@abstractmethod
def request(self):
pass

class RealSubject(Subject):
def request(self):
print("Реальный объект: Обработка запроса.")

class Proxy(Subject):
def __init__(self, real_subject):
self._real_subject = real_subject

def request(self):
if self.check_access():
self._real_subject.request()
self.log_access()

def check_access(self):
print("Заместитель: Проверка доступа перед выполнением запроса.")
return True

def log_access(self):
print("Заместитель: Логирование времени запроса.")

# Клиентский код
real_subject = RealSubject()
proxy = Proxy(real_subject)

proxy.request()


Ставь 👍 и забирай 📚 Базу знаний
👍2🔥1
🤔 Какие есть опции в свойстве on_delete?

on_delete в Django определяет поведение при удалении связанного объекта (например, ForeignKey).
Варианты:
- CASCADE — удаляет все связанные объекты.
- PROTECT — вызывает исключение, если есть связанные объекты.
- SET_NULL — обнуляет значение поля (если null=True).
- SET_DEFAULT — устанавливает значение по умолчанию.
- SET(...) — можно передать функцию или значение.
- DO_NOTHING — ничего не делает (может привести к ошибке на уровне БД).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍1💊1
🤔 Что за функция sleep()?

Функция sleep() из модуля time приостанавливает выполнение программы на заданное количество секунд.

🚩Как использовать `sleep()` в Python?

Функция sleep() принимает один аргумент** — число секунд (может быть дробным).
import time

print("Программа началась...")
time.sleep(3) # Ожидание 3 секунды
print("3 секунды прошло!")


🚩Где используется `sleep()`?

Ожидание в цикле (имитация загрузки)
for i in range(5, 0, -1):
print(i)
time.sleep(1) # Задержка 1 секунда между выводами
print("Старт!")


Запросы к серверу с паузами (чтобы не забанили)
import time
import requests

for i in range(3):
response = requests.get("https://example.com")
print(f"Запрос {i+1}: статус {response.status_code}")
time.sleep(2) # Ждём 2 секунды перед следующим запросом


Искусственная задержка перед повторной попыткой
for attempt in range(3):
print(f"Попытка {attempt + 1}...")
time.sleep(2) # Ожидание 2 секунды перед новой попыткой


Ставь 👍 и забирай 📚 Базу знаний
👍1💊1
🤔 Как получить из генератора список?

Можно просто передать генератор в функцию list(). Это принудительно извлечёт все элементы и вернёт их в виде списка.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
💊1
🤔 Что такое GET?

GET — это HTTP-метод, который используется для запроса данных с сервера.
Когда ты открываешь веб-сайт или вводишь URL в браузере — это GET-запрос. Браузер запрашивает страницу у сервера, и сервер возвращает данные.

🚩Как работает GET-запрос?

1⃣Клиент (браузер, программа) отправляет GET-запрос на сервер.
2⃣Сервер обрабатывает запрос и возвращает ответ (HTML-страницу, JSON-данные, картинку и т. д.).
3⃣Данные отображаются пользователю.

🚩Пример GET-запроса

Когда ты заходишь на https://example.com/users, браузер отправляет:
GET /users HTTP/1.1
Host: example.com


Ответ сервера
[
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
]


🚩Особенности GET-запроса

🟠Читаемый URL
параметры передаются в строке запроса (например, ?id=123).
🟠Безопасен
GET не изменяет данные на сервере.
🟠Можно кэшировать
браузеры и серверы могут сохранять результаты GET-запросов.
🟠Ограниченная длина URL
слишком длинные запросы могут не работать.
🟠Не подходит для конфиденциальных данных
передача пароля в URL (?password=123) небезопасна.

🚩GET-запрос с параметрами

Если нужно передать параметры, они добавляются в URL:
GET /search?q=python&page=2


В Python можно отправить GET-запрос с помощью библиотеки requests
import requests

response = requests.get("https://api.example.com/users", params={"id": 123})
print(response.json()) # Получаем ответ в JSON


Ставь 👍 и забирай 📚 Базу знаний
👍1
🤔 Множественное наследование?

Это возможность класса наследовать от нескольких родительских классов. В Python такая конструкция допустима и реализуется через порядок разрешения методов (MRO), но требует осторожности из-за потенциальных конфликтов.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍3
🤔 Назовите основные мидлвари, зачем они нужны

Middleware (промежуточное ПО) — это специальные классы, которые обрабатывают запросы и ответы, проходящие через Django. Они позволяют изменять данные, проверять доступ, логировать действия и многое другое.

🚩Основные мидлвари Django

🟠SecurityMiddleware
Добавляет важные HTTP-заголовки для защиты сайта:
- Strict-Transport-Security (HTTPS)
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY

🟠CommonMiddleware
Отвечает за:
Перенаправление с APPEND_SLASH=True (если /about → перенаправит на /about/).
Удаление www. (www.example.comexample.com).
Обработка кодировки и контента.

🟠SessionMiddleware
Позволяет Django хранить данные пользователя между запросами (например, авторизацию).
request.session["user_id"] = 42  # Сохраняем ID пользователя в сессии


🟠AuthenticationMiddleware
Позволяет работать с request.user, автоматически определяя пользователя.
if request.user.is_authenticated:
print(f"Пользователь: {request.user.username}")


🟠CSRF Middleware (CsrfViewMiddleware)
Защищает от атак межсайтовой подделки запросов (CSRF).
При обработке форм Django требует специальный CSRF-токен:
<form method="POST">
{% csrf_token %}
<input type="text" name="name">
</form>


🟠XFrameOptionsMiddleware
Запрещает встраивать сайт в <iframe>, предотвращая атаку Clickjacking.
X-Frame-Options: DENY


🟠MessageMiddleware
Позволяет передавать временные сообщения (django.contrib.messages).
from django.contrib import messages

messages.success(request, "Вы успешно вошли!")
messages.error(request, "Ошибка авторизации!")


🚩Как добавить или отключить мидлвари?

Мидлвари хранятся в settings.py:
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
]


🚩Как написать свой мидлвар?

Допустим, хотим логировать все запросы.
Создаём middleware.py
import datetime

class LogMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
print(f"[{datetime.datetime.now()}] Запрос: {request.path}")
response = self.get_response(request)
return response


Добавляем в settings.py
MIDDLEWARE.append("myapp.middleware.LogMiddleware")


Теперь в консоли будем видеть все запросы!
[2024-02-28 12:00:00] Запрос: /
[2024-02-28 12:01:00] Запрос: /login/


Ставь 👍 и забирай 📚 Базу знаний
👍2
🤔 Компоненты Django?

– Models — работа с базой данных,
– Views — логика обработки запросов,
– Templates — HTML-шаблоны,
– URLs — маршруты,
– Forms — формы и валидация,
– Admin — панель администратора,
– Middleware — перехватчики запросов/ответов.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍2
🤔 Как управлять кешированием в HTTP?

Кэширование в HTTP позволяет уменьшить нагрузку на сервер и ускорить загрузку страниц за счёт сохранения копий ресурсов.
Управление кэшем происходит через HTTP-заголовки, которые указывают, как долго хранить данные и когда обновлять их.

🟠Управление кэшированием через `Cache-Control`
Cache-Control — основной заголовок для кэширования, который указывает, как долго хранить ресурс и когда обновлять его.
Cache-Control: no-cache            # Браузер всегда запрашивает ресурс заново
Cache-Control: no-store # Запрещает кэширование вообще
Cache-Control: public, max-age=3600 # Кэшировать 1 час (3600 секунд)
Cache-Control: private, max-age=600 # Кэш только для одного пользователя (например, личный кабинет)
Cache-Control: must-revalidate # Клиент должен проверять, истёк ли срок кэша перед загрузкой


🟠Управление кэшем с `ETag` (оптимизированное обновление)
ETag — это уникальный идентификатор версии файла.
Сервер отправляет ресурс с ETag:
ETag: "abc123"

При следующем запросе браузер отправляет If-None-Match:
If-None-Match: "abc123"


Если ресурс не изменился, сервер отвечает 304 Not Modified (клиент использует кэш).
Если ресурс изменился — сервер отправляет новую версию.
HTTP/1.1 304 Not Modified


🟠Кэширование через `Last-Modified`
Работает аналогично ETag, но вместо идентификатора используется дата последнего изменения.
Сервер отправляет заголовок
Last-Modified: Wed, 21 Feb 2024 10:00:00 GMT


Браузер запрашивает ресурс с If-Modified-Since
If-Modified-Since: Wed, 21 Feb 2024 10:00:00 GMT


🟠Полное отключение кэша
Если нужно всегда загружать свежие данные, используем:
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache # Устарел, но нужен для старых браузеров
Expires: 0


🟠Управление кэшем через `Vary`
Если ресурс зависит от заголовков (User-Agent, Accept-Encoding), используем Vary.
Vary: User-Agent


🟠Принудительное обновление кэша (Cache Busting)
Если сервер отправил старый кэш, можно обновить ресурс с новым URL.
Способы
Добавить версию в URL
/style.css?v=2


Использовать хеш в имени файла:
/style.abc123.css


Ставь 👍 и забирай 📚 Базу знаний
👍2
🤔 Что такое dict comprehensions?

Это способ быстро создать словарь из другого итерируемого объекта, задав, как именно формируются ключи и значения. Такой подход делает код компактным и читаемым, особенно когда нужно создать словарь на основе списка или другого словаря.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍1
🤔 Что делать если нужно перехватить исключение, выполнить действия и опять возбудить это же исключение?

Если нужно:
Перехватить исключение
Выполнить какие-то действия (лог, очистка, уведомление и т. д.)
Снова выбросить это же исключение
try:
x = 1 / 0 # Ошибка деления на ноль
except ZeroDivisionError:
print("Ошибка! Записываем в лог...")
raise # Повторно выбрасываем то же исключение


Вывод
Ошибка! Записываем в лог...
Traceback (most recent call last):
File "script.py", line 2, in <module>
x = 1 / 0
ZeroDivisionError: division by zero


Пример: Логирование перед повторным выбросом
import logging

logging.basicConfig(filename="errors.log", level=logging.ERROR)

try:
user_input = int("abc") # Ошибка ValueError
except ValueError as e:
logging.error(f"Ошибка: {e}") # Записываем в лог
raise # Повторно выбрасываем исключение


Пример: Очистка ресурсов перед выбросом исключения
try:
file = open("data.txt", "r")
data = file.read()
except FileNotFoundError:
print("Файл не найден. Освобождаем ресурсы...")
raise # Снова выбрасываем исключение
finally:
file.close() # Гарантированно закроет файл


Ставь 👍 и забирай 📚 Базу знаний
🤔 При каких ошибках HTTP есть смысл ретраить?

Ретрай (повтор запроса) возможен при:
- 429 (Too Many Requests) — если сервер просит подождать.
- 503 (Service Unavailable) — сервер временно недоступен.
- 502/504 — сбои на уровне прокси/шлюзов, возможно временные.
Ретрай не имеет смысла при ошибках клиента (например, 400 или 404).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🔥4
🤔 Что такое set?

Это неупорядоченная коллекция уникальных элементов в Python. Это одна из встроенных структур данных языка, которая используется, когда вам нужно работать с наборами данных, исключая дубликаты и выполняя операции над множествами (например, пересечение, объединение и разность).

🚩Основные характеристики `set`

🟠Неупорядоченность
Элементы множества не имеют фиксированного порядка, то есть вы не можете обращаться к элементам по индексу, как в списках или кортежах.

🟠Уникальность элементов
Во множестве не может быть дубликатов. Если вы добавите во множество несколько одинаковых элементов, они будут храниться как один экземпляр.

🟠Изменяемость
Множества в Python изменяемы: вы можете добавлять, удалять и изменять их элементы. Однако сами элементы множества должны быть неизменяемыми (например, числа, строки, кортежи).

🟠Быстродействие
Операции проверки принадлежности (in), добавления и удаления элементов работают очень быстро, благодаря использованию хэш-таблиц в реализации множества.

🚩Создание множества

🟠Пустое множество
Для создания пустого множества используется функция set(), так как {} создаёт пустой словарь
empty_set = set()
print(empty_set) # Output: set()


🟠Создание множества с элементами
Вы можете передать список, строку, кортеж или другой итерируемый объект в функцию set().
# Создание множества из списка
numbers = set([1, 2, 3, 4, 5])
print(numbers) # Output: {1, 2, 3, 4, 5}

# Создание множества из строки (уникальные символы)
chars = set("hello")
print(chars) # Output: {'h', 'e', 'l', 'o'} (порядок может быть разным)


🟠Использование литералов
Вы также можете использовать фигурные скобки {} для создания множества
fruits = {"apple", "banana", "cherry"}
print(fruits) # Output: {'apple', 'banana', 'cherry'}


🚩Основные операции с множествами

🟠Добавление элементов
Используется метод add()
my_set = {1, 2, 3}
my_set.add(4)
print(my_set) # Output: {1, 2, 3, 4}


🟠Удаление элементов
remove() — удаляет элемент, выбрасывая ошибку, если его нет.
discard() — удаляет элемент, не выбрасывая ошибку, если его нет.
my_set = {1, 2, 3}
my_set.remove(2) # Удаляем элемент 2
print(my_set) # Output: {1, 3}

my_set.discard(5) # Ошибки не будет, если элемента 5 нет


pop() — удаляет и возвращает случайный элемент (так как множество неупорядочено)
my_set = {1, 2, 3}
removed_element = my_set.pop()
print(removed_element) # Например: 1
print(my_set) # Например: {2, 3}


🟠Очистка множества
my_set = {1, 2, 3}
my_set.clear()
print(my_set) # Output: set()


🟠Проверка наличия элемента
Используется оператор in
my_set = {1, 2, 3}
print(2 in my_set) # Output: True
print(5 in my_set) # Output: False


🚩Операции над множествами

Python поддерживает классические операции теории множеств:

🟠Объединение (`union` или `|`)
Возвращает множество, содержащее все элементы из двух множеств.
set1 = {1, 2, 3}
set2 = {3, 4, 5}
print(set1 | set2) # Output: {1, 2, 3, 4, 5}
print(set1.union(set2)) # То же самое


🟠Пересечение (`intersection` или `&`)
Возвращает элементы, которые присутствуют в обоих множествах.
print(set1 & set2)  # Output: {3}
print(set1.intersection(set2)) # То же самое


🟠Разность (`difference` или `-`)
Возвращает элементы, которые присутствуют только в одном множестве (а не в другом).
print(set1 - set2)  # Output: {1, 2} (только в set1)
print(set1.difference(set2)) # То же самое


🟠Симметрическая разность (`symmetric_difference` или `^`)
Возвращает элементы, которые есть в одном из множеств, но не в обоих сразу.
print(set1 ^ set2)  # Output: {1, 2, 4, 5}
print(set1.symmetric_difference(set2)) # То же самое


🚩Неизменяемое множество (`frozenset`)

Если вам нужно создать множество, которое нельзя изменить, используйте frozenset
frozen = frozenset([1, 2, 3])
print(frozen) # Output: frozenset({1, 2, 3})

# frozen.add(4) # Ошибка: 'frozenset' object has no attribute 'add'


Ставь 👍 и забирай 📚 Базу знаний
👍3
🤔 Какие неизменяемые типы данных есть?

К неизменяемым типам относятся: строки, числа (int, float), булевы значения, кортежи, frozenset, None и bytes. Такие объекты нельзя изменить после создания, любые операции с ними создают новые экземпляры.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Как в питоне реализуется многопоточность. Какими модулями?

Многопоточность в Python реализуется с помощью модуля threading, но из-за GIL (Global Interpreter Lock) потоки не могут выполняться параллельно на нескольких ядрах.

🟠Модуль `threading` (многопоточность, но с GIL)
Модуль threading позволяет запускать несколько потоков (threads) в одном процессе.
import threading
import time

def task(name):
print(f"{name} начал работу")
time.sleep(2) # Имитация задержки
print(f"{name} завершил работу")

# Создаём два потока
t1 = threading.Thread(target=task, args=("Поток 1",))
t2 = threading.Thread(target=task, args=("Поток 2",))

t1.start()
t2.start()

t1.join()
t2.join()
print("Все потоки завершены")


Вывод
Поток 1 начал работу
Поток 2 начал работу
(пауза 2 секунды)
Поток 1 завершил работу
Поток 2 завершил работу
Все потоки завершены


🟠Модуль `multiprocessing` (настоящая параллельность)
В отличие от threading, модуль multiprocessing создаёт отдельные процессы, которые могут выполняться на разных ядрах процессора.
import multiprocessing
import time

def task(name):
print(f"{name} начал работу")
time.sleep(2)
print(f"{name} завершил работу")

if __name__ == "__main__":
p1 = multiprocessing.Process(target=task, args=("Процесс 1",))
p2 = multiprocessing.Process(target=task, args=("Процесс 2",))

p1.start()
p2.start()

p1.join()
p2.join()
print("Все процессы завершены")


🟠Модуль `concurrent.futures` (более удобный API)
Этот модуль позволяет легко управлять потоками (ThreadPoolExecutor) и процессами (ProcessPoolExecutor).
from concurrent.futures import ThreadPoolExecutor
import time

def task(n):
time.sleep(2)
return f"Готово: {n}"

with ThreadPoolExecutor(max_workers=2) as executor:
results = executor.map(task, [1, 2, 3])

for result in results:
print(result)


Пример ProcessPoolExecutor (процессы)
from concurrent.futures import ProcessPoolExecutor

def square(n):
return n * n

with ProcessPoolExecutor() as executor:
results = executor.map(square, [1, 2, 3, 4])

print(list(results)) # [1, 4, 9, 16]


🟠Модуль `asyncio` (асинхронность, не потоки!)
Модуль asyncio не создаёт потоки или процессы, а работает через "корутины" и цикл событий (event loop).
import asyncio

async def task():
print("Начало")
await asyncio.sleep(2) # Не блокирует другие задачи
print("Конец")

async def main():
await asyncio.gather(task(), task())

asyncio.run(main())


Ставь 👍 и забирай 📚 Базу знаний
🔥1
🤔 Как узнать версию Python?

Через команду в терминале или встроенную переменную. Это позволяет узнать точную установленную версию интерпретатора.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔7👍1
🤔 Какие ограничения есть у рекурсии в Python?

Рекурсия — это мощный инструмент, но в Python она имеет ограничения, которые нужно учитывать при написании кода.

🟠Ограничение на глубину рекурсии (`sys.getrecursionlimit()`)
В Python по умолчанию рекурсия ограничена 1000 вызовами, чтобы избежать переполнения стека.
import sys
print(sys.getrecursionlimit()) # 1000 (обычное значение)


Если превысить этот лимит, программа вызовет ошибку
def recursive():
return recursive()

recursive() # RecursionError: maximum recursion depth exceeded


🚩Как изменить лимит?

Можно увеличить глубину рекурсии, но это небезопасно
sys.setrecursionlimit(2000)  # Увеличиваем до 2000


🟠Рекурсия требует много памяти
Каждый рекурсивный вызов создаёт новый фрейм в стеке вызовов.
def factorial(n):
if n == 1:
return 1
return n * factorial(n - 1)

print(factorial(10000)) # Ошибка из-за переполнения стека


🟠Отсутствие оптимизации хвостовой рекурсии
Другие языки (например, Lisp, JavaScript) автоматически оптимизируют хвостовую рекурсию (Tail Call Optimization, TCO).
Python не делает этого, поэтому даже "идеальная" рекурсия всё равно переполняет стек.
def tail_recursive(n, acc=1):
if n == 1:
return acc
return tail_recursive(n - 1, n * acc)

print(tail_recursive(1000)) # Всё равно вызовет RecursionError


🟠Рекурсия медленнее цикла
Рекурсивный вызов требует больше накладных расходов (создание стек-фреймов), чем обычный for или while.
# Итеративный вариант (быстрее)
def factorial_iter(n):
result = 1
for i in range(1, n + 1):
result *= i
return result

# Рекурсивный вариант (медленнее)
def factorial_rec(n):
if n == 1:
return 1
return n * factorial_rec(n - 1)


Ставь 👍 и забирай 📚 Базу знаний