В большинстве языков (C++, Java, JS), чтобы проверить, находится ли число в диапазоне, нужно писать два условия. В Python всё гораздо изящнее.
❌ Как пишут обычно:
age = 25
if age > 18 and age < 60:
print("Трудоспособный возраст")
✅ Как можно в Python:
age = 25
# Прямо как в математике!
if 18 < age < 60:
print("Трудоспособный возраст")
Это работает с любым количеством операторов:
x = 5
y = 10
z = 15
if x < y < z:
print("Порядок возрастания соблюден!")
and) — меньше.Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍3
Допустим, у вас есть список имен и список зарплат. Вам нужно вывести их парами. Не нужно использовать индексы!
В Python есть встроенная функция
zip(), которая "сшивает" списки как молнию на куртке.
names = ["Alice", "Bob", "Charlie"]
salaries = [50000, 60000, 70000]
# name берется из names, salary из salaries
for name, salary in zip(names, salaries):
print(f"{name} зарабатывает {salary}")
С помощью
zip можно мгновенно создать словарь из двух списков:
data_dict = dict(zip(names, salaries))
# {'Alice': 50000, 'Bob': 60000, ...}
Минимум кода — максимум пользы.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤1
Вам нужно узнать, сколько раз каждый символ встречается в строке или сколько раз слово встречается в списке? Не пишите циклы и словари вручную!
В модуле
collections есть идеальный инструмент — Counter.
from collections import Counter
text = "abracadabra"
stats = Counter(text)
print(stats)
# Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
Он работает как словарь, но умеет больше. Например, метод
.most_common() сразу покажет топ элементов:
# Вывести 2 самых частых символа
print(stats.most_common(2))
# [('a', 5), ('b', 2)]
Please open Telegram to view this post
VIEW IN TELEGRAM
:=Странное название, но очень полезная суть. Оператор
:= (он похож на глаза и клыки моржа) позволяет присвоить значение переменной прямо внутри выражения (например, внутри if или while).Задача:
Получить данные от функции (или пользователя) и проверить, не пустые ли они.
❌ Без моржа (3 строки):
text = input("Введите слово: ")
if len(text) > 5:
print(f"Длинное слово: {text}")
✅ С моржом (2 строки):
# Мы и присваиваем text, и сразу проверяем его длину
if len(text := input("Введите слово: ")) > 5:
print(f"Длинное слово: {text}")
Переменная
text останется доступной и внутри блока if, и после него. Это отлично экономит место и делает код компактнее.Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍6
Как запускать десятки сетевых запросов одновременно, не тормозя программу? Писать многопоточный код?
Используйте
async и await с библиотекой asyncio.import asyncio
async def fetch_data(n):
print(f"Запрос {n} стартовал")
await asyncio.sleep(1) # имитация сетевого запроса
print(f"Запрос {n} завершён")
return n * 2
async def main():
# запускаем все задачи одновременно
results = await asyncio.gather(*(fetch_data(i) for i in range(5)))
print("Результаты:", results)
asyncio.run(main())
asyncio.gather запускает все задачи параллельноPlease open Telegram to view this post
VIEW IN TELEGRAM
❤4👍2
Как выполнять математические операции над большими массивами без циклов Python? Писать
for и append? Используйте векторизацию NumPy.
import numpy as np
# создаём массив чисел
a = np.arange(1, 6)
b = np.arange(10, 15)
# сложение массивов поэлементно
c = a + b
print("Сумма массивов:", c)
# умножение каждого элемента на 2
d = a * 2
print("Умножение на 2:", d)
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤3
Как быстро подсчитать суммы, средние и максимум по категориям без цикла? Писать
for и if? Используйте
groupby и агрегации.import pandas as pd
# создаём DataFrame
data = pd.DataFrame({
"Компания": ["A", "B", "A", "B", "C"],
"Доход": [100, 200, 150, 120, 300],
"Расход": [50, 80, 60, 70, 150]
})
# группируем по компании и считаем суммарный доход и средний расход
summary = data.groupby("Компания").agg({
"Доход": "sum",
"Расход": "mean"
})
print(summary)
groupby позволяет группировать данные по ключуagg выполняет несколько операций сразуPlease open Telegram to view this post
VIEW IN TELEGRAM
👍7❤3
Как найти минимум функции или решить систему нелинейных уравнений, не переписывая всё вручную? Писать цикл и градиенты самому?
Используйте
scipy.optimize.from scipy.optimize import minimize, fsolve
import numpy as np
# --- Минимизация функции ---
# функция с минимумом
def func(x):
return (x - 3)**2 + np.sin(x)*5
# ищем минимум
res = minimize(func, x0=0) # x0 - начальное приближение
print("Минимум функции:", res.x, "Значение:", res.fun)
# --- Решение системы нелинейных уравнений ---
# система: x^2 + y^2 = 1, x - y = 0.5
def system(vars):
x, y = vars
return [x**2 + y**2 - 1, x - y - 0.5]
solution = fsolve(system, [0, 0]) # начальное приближение [x0, y0]
print("Решение системы:", solution)
minimize ищет минимум любой функции, можно задавать ограничения и методыfsolve решает системы нелинейных уравнений с произвольной сложностьюPlease open Telegram to view this post
VIEW IN TELEGRAM
❤3👍2
Как выбирать, изменять и считать элементы массивов без циклов, с полной гибкостью? Писать
for и if? Используйте булевы маски и broadcasting.
import numpy as np
# создаём массив случайных чисел
data = np.random.randint(0, 100, size=(5, 5))
print("Массив:\n", data)
# выбираем все элементы >50
mask = data > 50
print("Элементы >50:\n", data[mask])
# умножаем все >50 на 2 (broadcasting)
data[mask] *= 2
print("После умножения >50 на 2:\n", data)
# fancy indexing: выбираем 1, 3 и 4 строки, 0 и 2 столбцы
subset = data[[1,3,4], :][:, [0,2]]
print("Подмассив (fancy indexing):\n", subset)
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5❤2🥰2
Вам нужно найти общие элементы в двух списках или узнать, чем они отличаются? Не спешите писать вложенные циклы
for — это медленно и некрасиво.Используйте множества (
set). Они поддерживают математические операции "из коробки".Пример:
Есть два списка разработчиков:
frontend = {"Anna", "Bob", "Alice"}
backend = {"Bob", "John", "Alice"}
Что можно сделать одной строкой:
1️⃣ Кто фуллстек? (пересечение
&)
print(frontend & backend)
# {'Alice', 'Bob'}
2️⃣ Кто только фронтендер? (разность
-)
print(frontend - backend)
# {'Anna'}
3️⃣ Кто работает только на одной стороне? (симметрическая разность
^)
print(frontend ^ backend)
# {'Anna', 'John'}
Это работает очень быстро (почти мгновенно) даже на больших объемах данных.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍3
Вам наверняка приходилось писать код, где ошибку нужно просто проигнорировать.
Например, удалить файл, если он есть (а если нет — ну и ладно).
❌ Обычно это выглядит так:
import os
try:
os.remove("temp.txt")
except FileNotFoundError:
pass # Выглядит не очень красиво
✅ Pythonic way:
Используйте контекстный менеджер
suppress из модуля contextlib.
import os
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove("temp.txt")
Это делает намерение программиста очевидным: "Попробуй выполнить, но если вылетит эта ошибка — подави её". Код становится чище и читаемее.
#python #cleancode #refactoring #tips
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤3👎2
Допустим, у вас есть словарь, и нужно поменять местами ключи и значения (инвертировать его).
Исходные данные:
currencies = {'USD': 1, 'EUR': 0.9, 'GBP': 0.8}
Вместо того чтобы создавать пустой словарь и запускать цикл, используйте Dict Comprehension:
# {значение: ключ}
inverted = {value: key for key, value in currencies.items()}
print(inverted)
# {1: 'USD', 0.9: 'EUR', 0.8: 'GBP'}
Синтаксис
{k: v for ...} работает так же мощно, как и для списков. Можно даже добавить условия:
# Оставим только валюты с курсом меньше 1
cheap = {k: v for k, v in currencies.items() if v < 1}
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍3
Если у вас есть функция, которая выполняет тяжелые вычисления (или рекурсию) с одними и теми же аргументами, вы можете мгновенно её ускорить.
Импортируем декоратор `@lru_cache` из модуля
functools.Пример (классический Фибоначчи):
from functools import lru_cache
@lru_cache(maxsize=None) # <--- Вся магия здесь
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
print(fib(50))
Без кэша: Python будет считать это вечность (миллиарды вызовов).
С кэшем: Результат выведется мгновенно.
Декоратор запоминает результаты вызова функции. Если функцию вызовут снова с теми же аргументами, Python не будет считать заново, а просто возьмет готовый ответ из памяти.
#python #performance #optimization #functools
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6
Обычные кортежи (`tuple`) экономят память, но обращаться к данным по индексу — неудобно.
point[0] — что это? Координата X? Широта? ID пользователя?Используйте `NamedTuple` из модуля
typing (или `collections`). Это как класс, только легче.
from typing import NamedTuple
class Point(NamedTuple):
x: int
y: int
p = Point(10, 20)
# Теперь можно обращаться по имени!
print(p.x, p.y) # 10 20
# Но поведение кортежа сохраняется
print(p[0]) # 10
Это делает код "самодокументируемым". Идеально для возврата нескольких значений из функции.
#python #typing #structures #tips
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6
🤯 Математика с булевыми значениями
А вы знали, что в Python
Поэтому такой код абсолютно валиден:
Или даже так (используем как индекс):
💡 Примечание: в продакшене лучше так не писать, но знать об этом полезно!
#python #funfacts #underTheHood
А вы знали, что в Python
True и False — это на самом деле просто замаскированные 1 и 0? Тип bool наследуется от int.Поэтому такой код абсолютно валиден:
x = True + True + 5
print(x)
# Вывод: 7 (1 + 1 + 5)
Или даже так (используем как индекс):
options = ["Нет", "Да"]
is_agreed = True
print(options[is_agreed])
# Вывод: "Да" (потому что options[1])
#python #funfacts #underTheHood
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤3
Media is too big
VIEW IN TELEGRAM
В этом видео автор на практике разбирает, как взаимодействовать с внешними сервисами и данными через API. Вы научитесь отправлять GET и POST-запросы, обрабатывать ответы в формате JSON, а также работать с заголовками и параметрами с помощью универсальной библиотеки requests.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍2
Когда логи занимают гигабайты, а дампы БД не помещаются на диск, сжатие становится must-have. Но не нужно сжимать вручную — Python умеет работать со сжатыми файлами напрямую, как с обычными.
Модуль
gzip даёт тот же интерфейс, что и open(), но прозрачно сжимает:import gzip
import json
# ПИШЕМ сжатый JSON одной строкой
with gzip.open('data.json.gz', 'wt', encoding='utf-8') as f:
json.dump({'users': [{'id': i, 'name': f'user_{i}'} for i in range(10000)]}, f)
# Файл data.json.gz весит в 5-10 раз меньше
# ЧИТАЕМ как обычный файл
with gzip.open('data.json.gz', 'rt', encoding='utf-8') as f:
data = json.load(f)
print(f"Загружено {len(data['users'])} пользователей") # 10000
'rt'/'wt' для текста, 'rb'/'wb' для бинарных данных. Сжатие происходит на лету — не нужно держать в памяти весь несжатый файл.Для архивов, где важна степень сжатия, а не скорость:
import lzma
import pickle
# Сохраняем сжатый pickle
data = [{'x': i, 'y': i**2} for i in range(100000)]
with lzma.open('data.pickle.xz', 'wb') as f:
pickle.dump(data, f)
print("Файл сохранён с максимальным сжатием")
# Загружаем обратно
with lzma.open('data.pickle.xz', 'rb') as f:
loaded = pickle.load(f)
print(f"Загружено {len(loaded)} объектов")
lzma (формат .xz) сжимает лучше gzip, но работает медленнее. Идеально для долгосрочного хранения дампов.import gzip, lzma, os
import random
# Генерируем тестовые данные (повторяющиеся строки хорошо сжимаются)
text = '\n'.join(str(random.randint(1000, 9999)) for _ in range(50000))
# Сохраняем в разных форматах
with open('raw.txt', 'w') as f:
f.write(text)
with gzip.open('test.gz', 'wt') as f:
f.write(text)
with lzma.open('test.xz', 'wt') as f:
f.write(text)
# Сравниваем размеры
for name in ['raw.txt', 'test.gz', 'test.xz']:
print(f"{name}: {os.path.getsize(name)} байт")
# raw.txt: 345678 байт
# test.gz: 45678 байт (в ~7.5 раз меньше)
# test.xz: 23456 байт (в ~14.7 раз меньше)
lzma сжимает в 2-3 раза эффективнее gzip, но занимает больше CPU и времени. Выбирай по ситуации.Создадим универсальный инструмент, который сам определяет формат по расширению:
import bz2 # ещё один формат сжатия
def smart_open(filename, mode='r', **kwargs):
"""Открывает файл, автоматически определяя сжатие по расширению."""
if filename.endswith('.gz'):
return gzip.open(filename, mode, **kwargs)
elif filename.endswith('.xz') or filename.endswith('.lzma'):
return lzma.open(filename, mode, **kwargs)
elif filename.endswith('.bz2'):
return bz2.open(filename, mode, **kwargs)
else:
return open(filename, mode, **kwargs)
# Используем единообразно
with smart_open('data.json.gz', 'rt') as f:
data = json.load(f)
with smart_open('dump.sql', 'w') as f:
f.write('SELECT * FROM users;')
Сжимаем данные прямо при передаче по сети или в пайплайнах:
import gzip
import io
def compress_chunks(chunks):
"""Сжимает поток чанков на лету."""
buffer = io.BytesIO()
with gzip.GzipFile(fileobj=buffer, mode='wb') as f:
for chunk in chunks:
f.write(chunk.encode() if isinstance(chunk, str) else chunk)
buffer.seek(0)
return buffer
# Пример: сжимаем логи построчно
log_lines = [f"[INFO] Event {i}\n" for i in range(1000)]
compressed = compress_chunks(log_lines)
# Результат уже сжат
print(f"Исходно: {sum(len(l) for l in log_lines)} байт")
print(f"После сжатия: {len(compressed.getvalue())} байт")
gzip.open() для быстрого сжатия логов и lzma.open() для максимального сжатия дампов. Пиши контекстные менеджеры для единого интерфейса.Please open Telegram to view this post
VIEW IN TELEGRAM
❤8
Media is too big
VIEW IN TELEGRAM
try, except, finallyВ этом видео автор подробно разбирает, как предсказуемо управлять ошибками в программах с помощью конструкции try-except. Вы научитесь перехватывать конкретные типы исключений, корректно освобождать ресурсы в блоке
finally и создавать собственные классы ошибок для большей ясности кода.Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤2
Все пишут
SELECT и INSERT, но SQLite — это полноценная СУБД с фичами из больших баз. Используй RETURNING, UPSERT и оконные функции, чтобы логика работала на уровне данных, а не в коде.Классика: нужно обновить запись, если она есть, иначе создать. Раньше требовалось два запроса. Теперь — один:
import sqlite3
conn = sqlite3.connect(':memory:')
conn.execute('''CREATE TABLE users (id INTEGER PRIMARY KEY,
email TEXT UNIQUE,
login_count INTEGER)''')
# Первый вызов — вставит новую запись
conn.execute('''INSERT INTO users (email, login_count)
VALUES ('alice@mail.com', 1)
ON CONFLICT(email) DO UPDATE
SET login_count = login_count + 1''')
# Второй вызов — увеличит счётчик
conn.execute('''INSERT INTO users (email, login_count)
VALUES ('alice@mail.com', 1)
ON CONFLICT(email) DO UPDATE
SET login_count = users.login_count + 1''')
result = conn.execute('SELECT * FROM users').fetchone()
print(result) # (1, 'alice@mail.com', 2)
ON CONFLICT DO UPDATE — магия UPSERT. Ключевое поле должно иметь ограничение UNIQUE или PRIMARY KEY.Раньше чтобы получить ID новой записи, делали отдельный
SELECT. Теперь всё сразу:# Возвращаем ID и данные новой записи
cursor = conn.execute('''INSERT INTO users (email, login_count)
VALUES ('bob@mail.com', 1)
RETURNING id, email''')
new_user = cursor.fetchone()
print(f'Новый ID: {new_user[0]}, email: {new_user[1]}')
# Работает и с UPDATE/DELETE
cursor = conn.execute('''UPDATE users
SET login_count = login_count * 2
WHERE email = 'alice@mail.com'
RETURNING email, login_count''')
updated = cursor.fetchone()
print(f'Обновлено: {updated[0]}, новые логины: {updated[1]}')
RETURNING возвращает строки, затронутые INSERT, UPDATE или DELETE. Идеально для логирования и цепочек операций.Нужно пронумеровать строки, посчитать скользящее среднее или ранжировать — делай одним запросом:
# Создадим таблицу с продажами
conn.execute('''CREATE TABLE sales (
id INTEGER PRIMARY KEY,
product TEXT,
amount REAL,
sale_date DATE
)''')
# Заполняем тестовыми данными
sales_data = [
('Ноутбук', 1000, '2024-01-15'),
('Ноутбук', 1200, '2024-01-20'),
('Мышь', 50, '2024-01-10'),
('Мышь', 45, '2024-01-18'),
('Клавиатура', 80, '2024-01-12'),
]
conn.executemany('INSERT INTO sales (product, amount, sale_date) VALUES (?, ?, ?)', sales_data)
# Ранжируем товары по убыванию суммы продаж внутри каждой категории
cursor = conn.execute('''
SELECT
product,
amount,
sale_date,
ROW_NUMBER() OVER (PARTITION BY product ORDER BY amount DESC) as rank_in_product,
SUM(amount) OVER (PARTITION BY product) as total_per_product,
AVG(amount) OVER (ORDER BY sale_date ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) as moving_avg
FROM sales
ORDER BY product, amount DESC
''')
for row in cursor.fetchall():
print(row)
OVER (PARTITION BY ... ORDER BY ...) создаёт "окно" для вычислений. Не путать с GROUP BY — здесь строки не сворачиваются.RETURNING чтобы не делать лишних запросов, а оконные функции — для аналитики прямо в БД.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤3
Твой Docker-образ весит 1.5 ГБ, а сборка каждый раз тянет все зависимости с нуля? Пора это исправить. Правильный Dockerfile экономит время, трафик и нервы.
Главное правило: никогда не использовать
python:latest. Он огромный.# ❌ Плохо: 1 ГБ+
FROM python:3.11
# ✅ Хорошо: ~200 МБ
FROM python:3.11-slim
# ✅ Для фанатов минимализма: ~100 МБ (но могут быть проблемы с компиляцией)
FROM python:3.11-alpine
slim — урезанный Debian, alpine — минимальный Linux на musl libc. Для большинства проектов хватает slim.Зачем тащить в продакшен компиляторы и исходники?
# Этап 1: сборка
FROM python:3.11-slim as builder
WORKDIR /app
COPY requirements.txt .
# Создаём один слой со всеми зависимостями
RUN pip install --user --no-warn-script-location -r requirements.txt
# Этап 2: финальный образ
FROM python:3.11-slim
WORKDIR /app
# Копируем только установленные пакеты из builder
COPY --from=builder /root/.local /root/.local
# Копируем код
COPY . .
# Важно: добавляем путь к пакетам
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "main.py"]
builder.🧪 Оптимизация кэширования зависимостей: почему Docker снова качает pandas?
Docker кэширует слои. Если требования не меняются — не переустанавливай.
FROM python:3.11-slim
WORKDIR /app
# 1. Копируем ТОЛЬКО requirements.txt
COPY requirements.txt . # Этот слой кэшируется отдельно!
# 2. Устанавливаем зависимости
RUN pip install --no-cache-dir -r requirements.txt
# 3. Копируем весь остальной код
COPY . . # Этот слой пересобирается при любом изменении кода
CMD ["python", "main.py"]
RUN pip install... до тех пор, пока requirements.txt не изменится. Изменил одну строчку в коде — зависимости не переустанавливаются.🔍 Убираем мусор: один RUN, меньше слоев
Каждая команда в Dockerfile создаёт слой. Объединяй!
# ❌ Плохо: 4 слоя, остаётся мусор
RUN apt-get update
RUN apt-get install -y gcc
RUN pip install pandas
RUN apt-get remove -y gcc
# ✅ Хорошо: 1 слой, чисто
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc && \
pip install pandas && \
apt-get remove -y gcc && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/* # Чистим кэш apt
&&. Флаг --no-install-recommends ставит только необходимые пакеты. rm -rf /var/lib/apt/lists/* удаляет кэш пакетов.Собираем FastAPI-приложение с зависимостями, включая компилируемые.
# Этап сборки
FROM python:3.11-slim as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-warn-script-location -r requirements.txt
# Этап рантайма
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 # Отключаем .pyc файлы
# Не запускай от root!
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
--no-warn-script-location чтобы не было предупреждений. PYTHONDONTWRITEBYTECODE=1 ускоряет запуск и экономит место. Запуск от не-root пользователя повышает безопасность.slim-образы, многоступенчатую сборку и правильное кэширование зависимостей. Объединяй команды в один RUN и убирай за собой. Итоговый образ должен весить в 5-10 раз меньше первоначального.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10❤3
with open()`. Пишем свои для транзакций и таймеровКонтекстные менеджеры в Python — это магия
with, которая управляет жизненным циклом ресурсов. Все знают про файлы, но настоящая сила открывается, когда пишешь свои менеджеры для транзакций, замеров времени и автоматического отката.Любой контекстный менеджер — это класс с методами
__enter__ и __exit__.class MyManager:
def __enter__(self):
print("Входим в контекст")
return self # Этот объект попадёт в as
def __exit__(self, exc_type, exc_val, exc_tb):
print("Выходим из контекста")
return False # Если True — исключение будет подавлено
with MyManager() as mgr:
print("Внутри контекста")
__enter__ выполняется при входе в with, __exit__ — всегда при выходе, даже если было исключение.Представь, что нужно гарантировать откат изменений при ошибке:
import sqlite3
class Transaction:
def __init__(self, conn):
self.conn = conn
def __enter__(self):
self.conn.execute("BEGIN")
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
self.conn.commit()
print("✅ Коммит")
else:
self.conn.rollback()
print("❌ Откат из-за ошибки:", exc_val)
return False # Пробрасываем исключение дальше
# Использование
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE users (id INTEGER, name TEXT)")
try:
with Transaction(conn) as tx:
tx.execute("INSERT INTO users VALUES (1, 'Alice')")
raise ValueError("Искусственная ошибка!")
except:
pass
print(conn.execute("SELECT * FROM users").fetchall()) # []
with вылетает исключение — __exit__ делает rollback. Нет ошибки — commit.Идеально для быстрого профайлинга кусков кода:
import time
from contextlib import contextmanager
class Timer:
def __enter__(self):
self.start = time.perf_counter()
return self
def __exit__(self, *args):
self.end = time.perf_counter()
self.elapsed = self.end - self.start
print(f"⏱️ Время выполнения: {self.elapsed:.4f} сек")
with Timer():
data = [x**2 for x in range(1_000_000)]
# ⏱️ Время выполнения: 0.0453 сек
perf_counter() даёт высокоточное время. Менеджер можно расширить, чтобы сохранять результаты в лог.Через
__init__ передаём настройки, а в __enter__ — применяем:class LogToFile:
def __init__(self, filename, mode="a"):
self.filename = filename
self.mode = mode
def __enter__(self):
self.file = open(self.filename, self.mode)
print(f"📂 Файл {self.filename} открыт")
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
print(f"📂 Файл {self.filename} закрыт")
with LogToFile("app.log") as log:
log.write("Старт сессии\n")
Писать класс каждый раз долго. Есть синтаксический сахар:
from contextlib import contextmanager
@contextmanager
def temporary_file(content):
import tempfile
tmp = tempfile.NamedTemporaryFile(delete=False, mode="w")
try:
tmp.write(content)
tmp.close()
yield tmp.name # Внутри with доступно это значение
finally:
import os
os.unlink(tmp.name) # Удаляем файл после выхода
with temporary_file("секретные данные") as fpath:
print(f"Файл создан: {fpath}")
# Файл уже удалён
yield — это __enter__, всё после — __exit__. Идеально для одноразовых менеджеров.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6❤5