en
Feedback
Python для начинающих

Python для начинающих

Open in Telegram

Python для начинающих

Show more
1 240
Subscribers
No data24 hours
+17 days
+130 days
Posts Archive
Работа со структурами путей с помощью модуля pathlib
Работа со структурами путей с помощью модуля pathlib

### Понимание GIL и влияние на многопоточность в CPython Если вы пробовали разогнать Python многопоточностью и не получили ускорения — вы почти наверняка столкнулись с GIL. Давайте разберёмся, что это такое и когда он действительно мешает. #### Что такое GIL простыми словами GIL (Global Interpreter Lock) — это глобальная «блокировка интерпретатора» в CPython. Она гарантирует, что байт-код Python выполняется только одним потоком одновременно, даже если у вас 8 ядер и 32 потока. Зачем это сделано: - упрощает работу со сборщиком мусора; - упрощает реализацию многих внутренних структур; - делает интерпретатор проще и стабильнее. Цена — ограниченная масштабируемость по CPU в многопоточных программах на чистом Python-коде. #### Важный нюанс: CPU-bound vs I/O-bound Многопоточность в CPython не бесполезна. Важно, что именно делает ваш код: - CPU-bound (много вычислений на процессоре) — GIL чаще всего убивает выгоду от потоков. - I/O-bound (ожидание диска, сети, БД) — потоки отлично помогают прятать ожидание. #### Классический пример, где потоки не ускоряют
import threading
import time

def cpu_heavy(n):
    count = 0
    for _ in range(n):
        count += 1
    return count

def run_threads():
    t1 = threading.Thread(target=cpu_heavy, args=(10_000_000,))
    t2 = threading.Thread(target=cpu_heavy, args=(10_000_000,))

    start = time.time()
    t1.start(); t2.start()
    t1.join(); t2.join()
    print("Threads:", time.time() - start)

def run_single():
    start = time.time()
    cpu_heavy(10_000_000)
    cpu_heavy(10_000_000)
    print("Single:", time.time() - start)

if __name__ == "__main__":
    run_single()
    run_threads()
На большинстве машин время выполнения будет похоже: два потока не нагружают два ядра независимо, потому что GIL не даёт им параллельно исполнять Python-байткод. #### Где потоки действительно помогают Когда поток ждёт I/O, многие C-функции освобождают GIL. Это позволяет другому потоку работать. Классический пример — сеть:
import threading
import requests
import time

urls = [
    "https://httpbin.org/delay/2",
    "https://httpbin.org/delay/2",
    "https://httpbin.org/delay/2",
]

def fetch(url):
    resp = requests.get(url)
    print(url, resp.status_code)

def run_threads():
    start = time.time()
    threads = [threading.Thread(target=fetch, args=(u,)) for u in urls]
    for t in threads: t.start()
    for t in threads: t.join()
    print("Threads:", time.time() - start)

def run_single():
    start = time.time()
    for u in urls:
        fetch(u)
    print("Single:", time.time() - start)

if __name__ == "__main__":
    run_single()
    run_threads()
Здесь многопоточность почти наверняка будет существенно быстрее, потому что пока один поток ждёт ответ сервера, другой может выполняться. #### Как обходят ограничения GIL - multiprocessing — запускает несколько процессов, у каждого свой интерпретатор и свой GIL. - Расширения на C / Cython — тяжёлые вычисления выполняются в C-коде, который может освобождать GIL. - Для I/O-нагруженных задач — потоки или asyncio. Главная мысль: GIL — не баг, а особенность CPython. Он не запрещает многопоточность, но заставляет вас осознанно выбирать инструмент: потоки для I/O, процессы и C-расширения — для тяжёлых вычислений.

Понимание GIL и влияние на многопоточность в CPython
Понимание GIL и влияние на многопоточность в CPython

Python для начинающих: как подружиться с mixin‑классами и перестать копировать код Когда в проекте начинают повторяться одни и те же методы в разных классах, рука сама тянется к копипасте. Но Python предлагает более элегантный способ — mixin‑классы. Это маленькие «модули поведения», которые добавляют функциональность к классам, не превращая иерархию наследования в монстра. ### Что такое mixin? Mixin — это класс, который: - не самодостаточен (обычно не создают SomeMixin() напрямую), - добавляет узкоспециализированное поведение к другим классам, - используется через множественное наследование. Простейший пример: хотим, чтобы разные классы умели превращаться в словарь.
class AsDictMixin:
    def as_dict(self):
        return {
            key: value
            for key, value in self.__dict__.items()
            if not key.startswith("_")
        }
Теперь можем «подмешать» его к любому классу:
class User(AsDictMixin):
    def __init__(self, username, email):
        self.username = username
        self.email = email

class Product(AsDictMixin):
    def __init__(self, name, price):
        self.name = name
        self.price = price

u = User("alice", "alice@example.com")
p = Product("Keyboard", 99.9)

print(u.as_dict())
print(p.as_dict())
Один mixin — две разные модели, никакого дублирования кода. ### Миксины для логирования Полезный паттерн — миксин, добавляющий логирование:
import datetime as dt

class LoggerMixin:
    def log(self, message):
        timestamp = dt.datetime.now().isoformat(timespec="seconds")
        cls_name = self.__class__.__name__
        print(f"[{timestamp}] {cls_name}: {message}")
Применяем:
class PaymentService(LoggerMixin):
    def pay(self, amount):
        self.log(f"Start payment: {amount}")
        # payment logic...
        self.log("Payment done")

class NotificationService(LoggerMixin):
    def send_email(self, to, subject):
        self.log(f"Send email to={to}, subject={subject}")
        # email logic...

ps = PaymentService()
ps.pay(100)

ns = NotificationService()
ns.send_email("bob@example.com", "Hello")
Один раз написали LoggerMixin — пользуемся в любом сервисе. ### Несколько mixin'ов сразу Миксины отлично комбинируются:
class StrReprMixin:
    def __repr__(self):
        return f"<{self.__class__.__name__} {self.__dict__}>"

class Model(AsDictMixin, StrReprMixin):
    pass

class Order(Model, LoggerMixin):
    def __init__(self, order_id, total):
        self.order_id = order_id
        self.total = total

o = Order(123, 250)
o.log("Created")
print(o)
print(o.as_dict())
Order сразу получает: - as_dict из AsDictMixin, - красивый __repr__ из StrReprMixin, - логирование из LoggerMixin. ### Когда mixin — это плохо Использовать mixin'ы стоит, если: - поведение маленькое и изолированное (логирование, сериализация, кеш, валидация), - нужно повторное использование в разных несвязанных классах. Не стоит: - прятать в mixin'ы огромную бизнес‑логику, - городить глубоко вложенное множественное наследование — отладка станет адом. Mixin — это как маленький модуль суперспособностей: добавляет силам класса остроты, но сам по себе жить не должен. Если помнить об этом, код становится и чище, и приятнее в поддержке.

Как создавать и применять mixin-классы для повторного использования кода
Как создавать и применять mixin-классы для повторного использования кода

Организация структуры проекта с использованием __init__.py Когда маленький скрипт превращается в проект, главный враг — хаос: длинные файлы, дублирование кода, бесконечные импорты. Здесь на сцену выходит скромный файл __init__.py, который помогает превратить папку в «умный» модуль и организовать код по-взрослому. --- ### Что делает __init__.py 1. Показывает Python, что папка — это пакет (module package). 2. Управляет тем, что именно будет импортироваться из пакета. 3. Позволяет удобно группировать и переэкспортировать функции и классы. Простейшая структура проекта может выглядеть так:
my_project/
    app/
        __init__.py
        math_utils.py
        string_utils.py
    main.py
app — пакет. Без __init__.py старая версия Python просто не распознала бы его как пакет, а в новых версиях вы теряете удобство настройки импорта. --- ### Пример: переэкспорт удобного интерфейса math_utils.py:
def add(a, b):
    return a + b

def mul(a, b):
    return a * b
string_utils.py:
def to_upper(text):
    return text.upper()

def split_words(text):
    return text.split()
Если импортировать так:
from app.math_utils import add
from app.string_utils import to_upper
— импортов быстро станет много. Вместо этого настроим «витрину» в app/__init__.py:
from .math_utils import add, mul
from .string_utils import to_upper

__all__ = ["add", "mul", "to_upper"]
Теперь в main.py:
from app import add, mul, to_upper

result = add(2, 3)
text = to_upper("hello")
__all__ определяет, что попадет при from app import * и служит документированным интерфейсом пакета. --- ### Инициализация пакета __init__.py — обычный Python-файл. В нем можно выполнять код при первом импорте пакета: логирование, проверку окружения, загрузку конфигурации.
# app/__init__.py
print("App package initialized")

from .math_utils import add
Импорт:
import app  # выведет: App package initialized
Злоупотреблять этим не стоит: тяжелые операции (запросы в БД, сеть) лучше выносить в явные функции, но легкую инициализацию делать можно. --- ### Локальные и относительные импорты Внутри пакета используйте относительные импорты:
# app/string_utils.py
from .math_utils import add

def add_length(text, n):
    return len(text) + add(n, 0)
Точка . означает «из текущего пакета». Это делает пакет переносимым: вы можете переименовать app в core — внутренние импорты не сломаются. --- ### Итог __init__.py — не просто «пустой файл для галочки». Он: - превращает папку в пакет; - формирует чистый, продуманный интерфейс кода; - помогает контролировать импорты и структуру проекта. Если ваш проект растет — начните с осознанного __init__.py. Это маленький шаг в файле, но большой шаг к аккуратному, поддерживаемому коду.

Организация структуры проекта с использованием init.py
Организация структуры проекта с использованием init.py

Использование модификаторов доступа в классах: public, private, protected В Python нет классических модификаторов доступа как в Java или C++ — никаких public, private и protected в синтаксисе. Но есть соглашения и немного магии с подчеркиваниями, которые позволяют удобно управлять доступом к данным внутри класса. --- ## Public: по умолчанию открыто Все атрибуты без подчеркиваний считаются публичными.
class User:
    def __init__(self, name):
        self.name = name  # public

    def greet(self):
        return f"Hello, {self.name}!"

u = User("Alice")
print(u.name)        # OK
print(u.greet())     # OK
Публичные атрибуты — часть интерфейса класса. Менять их снаружи — нормально (если это предусмотрено логикой). --- ## Protected: одно подчеркивание Одно подчеркивание — это сигнал разработчикам: "не трогайте это снаружи без необходимости". Это не защита, а соглашение.
class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner       # public
        self._balance = balance  # protected (по соглашению)

    def deposit(self, amount):
        self._balance += amount

    def get_balance(self):
        return self._balance

acc = BankAccount("Bob", 100)
acc.deposit(50)
print(acc.get_balance())  # 150

# Технически можно, но «не по правилам»:
acc._balance = 1_000_000
Чаще всего такие атрибуты используются для наследования: дочерний класс может работать с _balance, но внешний код — по идее нет. --- ## Private: двойное подчеркивание и name mangling Двойное подчеркивание включает механизм name mangling: имя переписывается в _ИмяКласса__атрибут. Это защищает от случайного доступа и конфликтов в наследовании.
class SecureAccount:
    def __init__(self, pin):
        self.__pin = pin  # private

    def check_pin(self, pin):
        return pin == self.__pin

sa = SecureAccount("1234")
print(sa.check_pin("0000"))  # False

# Прямой доступ не сработает:
# print(sa.__pin)  # AttributeError

# Но Python не ставит абсолютных стен:
print(sa._SecureAccount__pin)  # "1234"
Использовать __name стоит, когда вы: - хотите защититься от случайного переопределения в наследниках; - явно обозначаете: "это строго внутренняя деталь реализации". --- ## Как выбирать? - Публичное (name): часть API, с этим могут работать все. - Protected (_name): внутренняя деталь, можно трогать лишь в крайних случаях и обычно внутри иерархии классов. - Private (__name): скрытая реализация, которую вы не планируете трогать извне и в наследниках. Главное: в Python модификаторы — это не про «запрет», а про договор между разработчиками. Уважайте подчеркивания — и ваш код станет чище и понятнее.

Использование модификаторов доступа в классах: public, private, protected
Использование модификаторов доступа в классах: public, private, protected

Как распарсить текстовое меню и извлечь информацию Иногда нам достается меню не в виде аккуратного JSON, а просто куском текста: письмо от администратора, скопированное меню из чата или лог старой системы. Но даже из такого «хаоса» можно вытащить структурированные данные с помощью Python. Представим, что у нас есть такое меню:
1. Margherita - 350
2. Pepperoni - 420
3. Four Cheese - 500
4. Vegan Special - 390
Наша цель — превратить это в список словарей: {"name": ..., "price": ...}. ### Вариант 1: Базовый парсинг со split Для очень простых и предсказуемых форматов можно обойтись без регулярных выражений:
menu_text = """
1. Margherita - 350
2. Pepperoni - 420
3. Four Cheese - 500
4. Vegan Special - 390
"""

items = []
for line in menu_text.strip().splitlines():
    if not line.strip():
        continue
    # "1. Margherita - 350" -> ["1. Margherita ", " 350"]
    left, price_str = line.split('-')
    price = int(price_str.strip())
    # "1. Margherita " -> ["1.", "Margherita"]
    _, name = left.split('.', maxsplit=1)
    items.append({
        "name": name.strip(),
        "price": price
    })

print(items)
Результат:
[
    {'name': 'Margherita', 'price': 350},
    {'name': 'Pepperoni', 'price': 420},
    ...
]
Такой подход хорош, когда формат жёстко фиксирован и вы его контролируете. ### Вариант 2: Регулярные выражения для «живого» текста Если в меню могут быть пробелы, произвольные номера позиций или даже валюта, лучше использовать re:
import re

menu_text = """
1) Margherita - 350 RUB
02. Pepperoni — 420
#3 Four Cheese: 500р
4 Vegan Special 390
"""

pattern = re.compile(
    r"""
    ^\D*?(\d+)\D+        # номер блюда
    ([A-Za-z ]+?)\D+     # название
    (\d+)\s*             # цена
    """,
    re.VERBOSE
)

items = []
for line in menu_text.strip().splitlines():
    match = pattern.search(line)
    if not match:
        continue
    pos, name, price = match.groups()
    items.append({
        "position": int(pos),
        "name": name.strip(),
        "price": int(price)
    })

print(items)
re.VERBOSE позволяет красиво документировать шаблон и не превращать его в нечитаемое «простыню» из символов. ### Вариант 3: Превращаем парсер в функцию Хорошая практика — оборачивать логику парсинга в функцию: потом вы сможете менять реализацию, не трогая остальной код.
def parse_menu(text: str):
    import re
    pattern = re.compile(r'^\s*\d+\D+([A-Za-z ]+)\D+(\d+)', re.MULTILINE)
    result = []
    for name, price in pattern.findall(text):
        result.append({
            "name": name.strip(),
            "price": int(price)
        })
    return result

menu_items = parse_menu(menu_text)
Дальше с этим списком уже можно делать всё что угодно: считать среднюю цену, фильтровать по бюджету, строить API или генератор PDF-меню. Парсинг текстовых меню — отличный тренировочный полигон: вы прокачиваете строки, регулярные выражения, функции и немного алгоритмическое мышление. А заодно перестаёте бояться «грязных» данных: Python умеет наводить порядок.

Как распарсить текстовое меню и извлечь информацию
Как распарсить текстовое меню и извлечь информацию

Создание простого API‑клиента с requests и json Рано или поздно каждый питонист сталкивается с задачей: “Нужно что‑то забрать из интернета… программно”. Тут на сцену выходит связка requests + json — минимальный набор, чтобы подружиться с любым более‑менее современным веб‑сервером. --- ### Что такое API и зачем нам клиент? API — это “договор” о том, как общаться с сервисом: по какому адресу стучаться, какие параметры передавать, в каком формате будет ответ. Чаще всего ответ приходит в формате JSON — структуре, очень похожей на словари и списки Python. Наша задача: 1. Отправить HTTP‑запрос (GET, POST, …). 2. Получить ответ. 3. Превратить JSON в привычные объекты Python. --- ### Первый запрос: получаем данные Установим библиотеку:
pip install requests
Простейший клиент:
import requests

url = "https://jsonplaceholder.typicode.com/posts/1"
response = requests.get(url)

print(response.status_code)  # 200?
print(response.text[:100])   # сырой текст ответа
Но текст — это неудобно. Лучше сразу разобрать JSON:
import json
import requests

url = "https://jsonplaceholder.typicode.com/posts/1"
response = requests.get(url)

if response.status_code == 200:
    data = json.loads(response.text)
    # эквивалентно: data = response.json()
    print(type(data))     # <class 'dict'>
    print(data["title"])  # доступ к полям, как в обычном словаре
else:
    print("Request failed:", response.status_code)
--- ### Параметры запроса и мини‑клиент Обычно API принимает параметры. Например, выбираем несколько постов:
import requests

BASE_URL = "https://jsonplaceholder.typicode.com"

def get_posts(limit=5):
    params = {"_limit": limit}
    response = requests.get(f"{BASE_URL}/posts", params=params)
    response.raise_for_status()  # выбросит исключение при ошибке
    return response.json()

posts = get_posts(limit=3)
for post in posts:
    print(f"{post['id']}: {post['title']}")
Здесь важно: - params — словарь, который requests превращает в query‑строку (?key=value). - response.raise_for_status() — простой способ не забыть про обработку ошибок. --- ### Отправка данных: POST с JSON Иногда нужно не только читать, но и отправлять информацию:
import requests

BASE_URL = "https://jsonplaceholder.typicode.com"

def create_post(title, body, user_id=1):
    payload = {
        "title": title,
        "body": body,
        "userId": user_id,
    }
    response = requests.post(f"{BASE_URL}/posts", json=payload)
    response.raise_for_status()
    return response.json()

new_post = create_post("Hello API", "This is my first API client!")
print(new_post)
Флаг json=payload: - автоматически превращает словарь в JSON; - выставляет заголовок Content-Type: application/json. --- ### На что обратить внимание дальше - Тайм-ауты: requests.get(url, timeout=5) - Заголовки: headers={"Authorization": "Bearer <token>"} - Логику удобно оборачивать в класс ApiClient, если эндпоинтов много. Этой базой уже можно за вечер прикрутить свой скрипт к реальному веб‑сервису: от погоды до телеграм‑ботов — везде вокруг API, а requests + json превращают их в обычные словари и списки Python.

Создание простого API-клиента с requests и json
Создание простого API-клиента с requests и json

Как выполнять задачи по расписанию с помощью schedule Иногда нужно, чтобы скрипт сам что‑то делал: раз в минуту проверял почту, каждый день делал бэкап или раз в час напоминал «пора сделать перерыв». Писать собственный планировщик — лишняя боль, когда есть простой модуль schedule. --- ### Установка
pip install schedule
--- ### Базовая идея schedule работает по принципу: 1. Определяем задачу — обычную функцию. 2. Назначаем ей расписание. 3. Запускаем бесконечный цикл, который проверяет, не пора ли что‑то выполнить. Простейший пример:
import schedule
import time


def send_report():
    print("Sending report...")


schedule.every(10).seconds.do(send_report)

while True:
    schedule.run_pending()
    time.sleep(1)
Здесь send_report() запускается каждые 10 секунд. run_pending() проверяет «наступило ли время» для задач, а sleep(1) просто не даёт циклу крутиться без паузы. --- ### Расписание посерьёзнее schedule умеет работать с минутами, часами, днями и даже днями недели:
import schedule
import time


def backup_db():
    print("Backup created")


def remind_break():
    print("Time to take a break!")


schedule.every().day.at("23:30").do(backup_db)      # каждый день в 23:30
schedule.every(15).minutes.do(remind_break)        # каждые 15 минут
schedule.every().monday.at("09:00").do(backup_db)  # каждый понедельник в 09:00

while True:
    schedule.run_pending()
    time.sleep(1)
Формат времени — строка "HH:MM" в 24‑часовом формате. --- ### Передача аргументов в задачи Функция‑задача может принимать аргументы:
import schedule
import time


def send_email(to, subject):
    print(f"Email to {to}: {subject}")


schedule.every().hour.do(send_email, to="admin@example.com",
                         subject="Hourly status")

while True:
    schedule.run_pending()
    time.sleep(1)
--- ### Управление задачами Иногда нужно отменить часть расписания:
import schedule


def clear_cache():
    print("Cache cleared")


job = schedule.every().hour.do(clear_cache)

# Удалить конкретную задачу
schedule.cancel_job(job)

# Удалить все задачи
schedule.clear()
--- ### Когда использовать schedule schedule отлично подходит для: - простых фоновых задач; - учебных проектов и прототипов; - скриптов, которые всегда крутятся на одном сервере/ПК. Для сложных продакшн‑систем чаще берут cron, Celery и подобные инструменты, но для начала schedule даёт очень удобный и наглядный вход в мир планировщиков задач.

Как выполнять задачи по расписанию с использованием schedule
Как выполнять задачи по расписанию с использованием schedule

Как выполнять задачи по расписанию с использованием schedule
Как выполнять задачи по расписанию с использованием schedule

Сортировка и фильтрация словарей по значениям и ключам Словарь в Python — отличный способ хранить данные вида «ключ → значение». Но реальная магия начинается, когда нужно отсортировать или отфильтровать этот хаос. Разберёмся, как это делать красиво и понятно. --- ### Сортировка словаря по ключам и значениям У нас есть словарь с результатами теста:
scores = {
    "alice": 85,
    "bob": 92,
    "charlie": 78,
    "david": 92
}
Сортировка по ключам (по имени):
sorted_by_name = dict(sorted(scores.items(), key=lambda item: item[0]))
print(sorted_by_name)
# {'alice': 85, 'bob': 92, 'charlie': 78, 'david': 92}
item[0] — это ключ (имя). sorted() возвращает список пар (key, value), а dict(...) снова собирает словарь. Сортировка по значениям (по баллам):
sorted_by_score = dict(sorted(scores.items(), key=lambda item: item[1]))
print(sorted_by_score)
# {'charlie': 78, 'alice': 85, 'bob': 92, 'david': 92}
item[1] — значение (баллы). Чтобы отсортировать по убыванию:
sorted_by_score_desc = dict(sorted(scores.items(), key=lambda item: item[1], reverse=True))
--- ### Сортировка с дополнительным критерием Представьте, что нужно отсортировать по баллам по убыванию, а при равных баллах — по имени по возрастанию.
sorted_complex = dict(sorted(
    scores.items(),
    key=lambda item: (-item[1], item[0])
))
print(sorted_complex)
# {'bob': 92, 'david': 92, 'alice': 85, 'charlie': 78}
Мы используем кортеж в качестве ключа сортировки: сначала минус баллы (для убывания), потом имя. --- ### Фильтрация словаря Допустим, нам нужны только те, у кого больше 80 баллов.
filtered_scores = {
    name: score
    for name, score in scores.items()
    if score > 80
}
print(filtered_scores)
# {'alice': 85, 'bob': 92, 'david': 92}
Это dict comprehension — мощный и читаемый способ фильтрации. Можно фильтровать и по ключам:
filtered_by_name = {
    name: score
    for name, score in scores.items()
    if name.startswith("a")
}
print(filtered_by_name)
# {'alice': 85}
--- ### Комбинируем: фильтрация + сортировка Например: взять только тех, у кого баллы > 80, и отсортировать по имени.
filtered = {
    name: score
    for name, score in scores.items()
    if score > 80
}

result = dict(sorted(filtered.items(), key=lambda item: item[0]))
print(result)
# {'alice': 85, 'bob': 92, 'david': 92}
Или вообще без промежуточной переменной:
result = dict(sorted(
    (
        (name, score)
        for name, score in scores.items()
        if score > 80
    ),
    key=lambda item: item[0]
))
--- Умение сортировать и фильтровать словари — один из тех навыков, который сразу делает код аккуратнее и понятнее. А дальше можно подключать operator.itemgetter, functools.partial и другие инструменты, но фундамент вы уже видите: sorted(), lambda, и comprehension — ваше основное оружие.

Сортировка и фильтрация словарей по значениям и ключам
Сортировка и фильтрация словарей по значениям и ключам

Как работать с очередями заданий с библиотекой queue Представьте, что у вас есть список задач, которые нужно выполнить: скачать файлы, обработать данные, отправить письма. Если делать всё по очереди в одном потоке — программа “тупо” ждёт завершения каждой операции. Очереди заданий позволяют распределить работу между несколькими потоками и не потерять ни одной задачи. В Python для этого есть модуль queue, который обеспечивает потокобезопасную очередь. --- ### Базовый пример: очередь задач
import queue

task_queue = queue.Queue()

# добавляем задачи
task_queue.put("download_file_1")
task_queue.put("download_file_2")
task_queue.put("process_data")

while not task_queue.empty():
    task = task_queue.get()
    print("Handling:", task)
    task_queue.task_done()
Queue() по умолчанию — неограниченная по размеру. Методы: - put(item) — добавить задачу - get() — взять задачу (если очереди пустая — ждёт) - task_done() — сообщить, что задача обработана - join() — подождать, пока все задачи будут помечены как выполненные --- ### Очередь + потоки: простой пул воркеров Типичный сценарий: один поток генерирует задачи, несколько потоков их обрабатывают.
import queue
import threading
import time

task_queue = queue.Queue()

def worker(worker_id):
    while True:
        task = task_queue.get()
        if task is None:  # сигнал остановки
            task_queue.task_done()
            break
        print(f"Worker {worker_id} handling {task}")
        time.sleep(0.5)  # имитация работы
        task_queue.task_done()

# запускаем 3 обработчика
workers = []
for i in range(3):
    t = threading.Thread(target=worker, args=(i,))
    t.start()
    workers.append(t)

# добавляем задачи
for n in range(10):
    task_queue.put(f"task_{n}")

# ждём завершения всех задач
task_queue.join()

# останавливаем воркеров
for _ in workers:
    task_queue.put(None)

for t in workers:
    t.join()
Ключевой момент: очередь сама заботится о синхронизации между потоками — вам не нужны явные блокировки. --- ### Другие типы очередей queue предлагает не только обычную FIFO-очередь: - queue.LifoQueue — стек (последний вошёл — первый вышел) - queue.PriorityQueue — приоритетная очередь Пример приоритетной очереди: задачи с меньшим числом выполняются раньше.
import queue

pq = queue.PriorityQueue()

pq.put((1, "low_priority"))
pq.put((0, "high_priority"))
pq.put((5, "very_low_priority"))

while not pq.empty():
    priority, task = pq.get()
    print(priority, task)
    pq.task_done()
--- queue — это простой способ построить систему обработки заданий: от игрушечного пула потоков до мини-очереди задач внутри вашей программы. Понимание этих примитивов — отличный шаг к написанию более масштабных и отзывчивых приложений на Python.

Как работать с очередями заданий с библиотекой queue
Как работать с очередями заданий с библиотекой queue