requests + BeautifulSoup: просто, быстро, эффективноЕсли тебе нужно вытянуть данные с HTML-страницы — это классика.
requests тянет страницу, BeautifulSoup разбирает её в структуру, которую легко обрабатывать.import requests
from bs4 import BeautifulSoup
url = "https://example.com"
resp = requests.get(url)
soup = BeautifulSoup(resp.text, "html.parser")
soup — это объект, с которым можно работать как с DOM-деревом.title = soup.find("h1").text
links = [a["href"] for a in soup.find_all("a") if "href" in a.attrs]find() — первый найденный элемент, find_all() — список всех. Атрибуты доступны как у словаря.items = soup.select("div.card > a.link")select() — поддерживает любые CSS-селекторы. Гибко и читаемо.for img in soup.select("img"):
print(img.get("src"), img.get("alt"))get() безопаснее прямого доступа: не упадёт, если атрибута нет.headers = {"User-Agent": "Mozilla/5.0"}
r = requests.get(url, headers=headers)User-Agent. Добавляй его для стабильности.requests + BeautifulSoup — это основа для быстрого парсинга HTML.Подходит для большинства задач: просто, мощно, читаемо. Но не забудь уважать
robots.txt и лимитировать частоту запросов.Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤1
• copy() создает поверхностную копию объекта (ссылки на вложенные элементы сохраняются).
• deepcopy() создает полную независимую копию объекта.
import copy
a = [[1, 2], [3, 4]]
b = copy.copy(a)
c = copy.deepcopy(a)
a[0][0] = 99
print(b) # [[99, 2], [3, 4]] (изменилось)
print(c) # [[1, 2], [3, 4]] (осталось прежним)
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
is и ==Многие путают
is и ==, но их предназначение разное:•
is проверяет, являются ли два объекта одной и той же сущностью в памяти.•
== проверяет, равны ли их значения.
a = [1, 2, 3]
b = a
c = [1, 2, 3]
print(a is b) # True (указывает на тот же объект)
print(a == b) # True (списки одинаковые)
print(a is c) # False (разные объекты)
print(a == c) # True (одинаковое содержимое)
is для проверки, ссылаются ли переменные на один объект, а == — для сравнения значений.Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍2
Когда не нужен полноценный PostgreSQL, а просто надо где-то хранить данные — SQLite идеально.
.db-файлеimport sqlite3
conn = sqlite3.connect("users.db") # создаёт файл, если нет
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
age INTEGER
)
""")
conn.commit()
users — без серверов, Docker и настроек.cursor.execute("INSERT INTO users (username, age) VALUES (?, ?)", ("alice", 30))
conn.commit()? — это защита от SQL-инъекций.cursor.execute("SELECT * FROM users")
for row in cursor.fetchall():
print(row)cursor.execute("UPDATE users SET age = ? WHERE username = ?", (31, "alice"))
cursor.execute("DELETE FROM users WHERE age < ?", (18,))
conn.commit()conn.close()
Или так:
with sqlite3.connect("users.db") as conn:
...SQLite — идеальный вариант для прототипов, локальных тулз, ботов, скриптов и парсеров.
Поддерживает SQL, транзакции, индексы — и всё в одном файле.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍2
Если вы часто пишете
print("var =", var), чтобы проверить значение переменной, то этот трюк сэкономит вам кучу времени.Начиная с Python 3.8, в f-строках можно использовать знак равно =.
user = "Alex"
age = 25
print(f"user = {user}, age = {age}")
# Вывод: user = Alex, age = 25
Просто добавьте = в конце переменной внутри фигурных скобок. Python сам подставит имя переменной и её значение.
user = "Alex"
age = 25
print(f"{user=}, {age=}")
# Вывод: user='Alex', age=25
Это работает даже с выражениями:
print(f"{2 + 2 = }")
# Вывод: 2 + 2 = 4
Мелочь, а код для отладки пишется в два раза быстрее! 🚀
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍3
try:
do_something()
except:
pass
⛔️ Это плохая практика, которая уничтожает отладку, ломает контроль потока и маскирует реальные ошибки.
-
KeyboardInterrupt — Ctrl+C не сработает, ты не сможешь остановить процесс.-
SystemExit — sys.exit() не завершит программу.-
SyntaxError, MemoryError, ImportError — всё будет съедено.Если внутри блока
try падает непредсказуемая ошибка, а ты её не логируешь — ты даже не узнаешь, что код работает неправильно.
try:
complex_logic()
except:
pass # ничего не видно, баг скрыт
Ты можешь:
- Ловить
AttributeError, потому что опечатался.- Не инициализировать переменную.
- Получать
KeyError, IndexError, ValueError — и никогда об этом не узнаешь.
try:
result = do_network_call()
except TimeoutError:
print("Превышено время ожидания")
Если ты не знаешь, какие ошибки может выбросить функция — выясни. Это важно.
import logging
try:
open("somefile.txt")
except FileNotFoundError as e:
logging.warning(f"Файл не найден: {e}")
Это поможет в отладке, особенно если ты на проде. Можно подключить
sentry, rollbar, loguru — что угодно, лишь бы не терять ошибки в пустоте.
try:
optional_feature()
except SomeError:
pass # ok to fail — не критично
Будущий ты (или твой тиммейт) скажет спасибо.
try:
...
except Exception:
pass
🧨
Exception — тоже широкая сеть. Иногда это допустимо (например, в retry-декораторе), но по умолчанию — нет.Ctrl+C, exit(). Иногда (но редко!) тебе нужно поймать всё — например, при логгировании в многопоточном приложении или глобальном хендлере ошибок:
try:
...
except Exception:
logger.exception("Что-то пошло не так")
Но всегда логируй, и никогда не делай
pass без причины.Backend, парсеры, ETL-пайплайны, API, CLI-инструменты — везде, где ошибка не должна оставаться в тени.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1👍1
with: Чистый код и надёжное управление ресурсамиЕщё одна мощная и идиоматичная конструкция в Python — это менеджеры контекста и оператор
with. Они кардинально упрощают работу с ресурсами, которые требуют обязательной "очистки" после использования, например, файлы, сетевые соединения или блокировки потоков.Представьте, что вы открываете файл для записи. Крайне важно его потом закрыть, иначе данные могут не сохраниться, а файловый дескриптор останется занят. Классический, но многословный способ гарантировать закрытие — использовать
try...finally:
f = open('my_file.txt', 'w')
try:
f.write('Важные данные!')
# ... тут может произойти ошибка
finally:
f.close() # Закроется в любом случае
Работает, но выглядит громоздко. Python предлагает элегантную альтернативу с
with:
with open('my_file.txt', 'w') as f:
f.write('Важные данные!')
# ... тут тоже может произойти ошибка
# Как только блок `with` завершается (нормально или из-за ошибки),
# Python АВТОМАТИЧЕСКИ вызывает f.close()!
В чём магия with?
Объекты, используемые с
with (как тот, что возвращает open()), реализуют так называемый "протокол менеджера контекста". У них есть специальные методы (__enter__ и __exit__). Оператор with вызывает __enter__ при входе в блок и гарантированно вызывает __exit__ при выходе из блока, передавая туда информацию об исключениях, если они были. Именно в __exit__ и происходит освобождение ресурса (закрытие файла).Оператор
with — это стандарт де-факто для управления ресурсами в Python. Он делает код не только короче, но и значительно безопаснее и понятнее.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3❤1👍1
Начиная с Python 3.10,
match/case позволяет не просто сравнивать, но разбирать структуры объектов.class Point:
def __init__(self, x, y): self.x, self.y = x, y
def handle(p):
match p:
case Point(x=0, y=y):
print(f"На оси Y: y={y}")
case Point(x=x, y=0):
print(f"На оси X: x={x}")
case Point():
print("Внутри плоскости")
handle(Point(0, 5))
Работает благодаря
__match_args__ — специальному атрибуту:class Point:
__match_args__ = ("x", "y")
match/case гораздо шире if — особенно при парсинге AST, JSON, конфигураций и DSL.Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1
🚰 Стриминг больших данных через генераторы и
Когда данных слишком много — миллионы строк в файле, бесконечные события или поток логов — держать всё в памяти нельзя. Тут спасают генераторы и ключевое слово
📦 Чтение файла построчно
➡️ Память не переполняется: читается и отдаётся по строке.
🔗 Комбинация генераторов через yield from
➡️
⚡️ Обработка пайплайнами
➡️ Логика разбивается на звенья, и каждое обрабатывает поток данных лениво.
🌀 Бесконечные источники
➡️ Генератор выдаёт значения бесконечно, но потребитель сам решает, когда остановиться.
📑 Вложенные пайплайны с
➡️
🚀 Генераторы + итераторы стандартной библиотеки
➡️ Вместо списка в миллион чисел получаешь поток, из которого берёшь только нужное.
🗣️ Запомни: генераторы — это ленивые конвейеры данных, yield from связывает их в цепочку. Такой подход позволяет обрабатывать гигабайты без переполнения памяти и писать код, который дышит как поток.
yield fromКогда данных слишком много — миллионы строк в файле, бесконечные события или поток логов — держать всё в памяти нельзя. Тут спасают генераторы и ключевое слово
yield from, позволяющее строить цепочки ленивых итераторов.def read_file(path):
with open(path, "r") as f:
for line in f:
yield line.strip()
for row in read_file("bigdata.txt"):
print(row)
def numbers():
yield from range(5)
yield from range(10, 15)
print(list(numbers()))
yield from разворачивает подгенераторы и прокидывает их значения наружу.def read_lines(path):
with open(path) as f:
yield from f
def filter_errors(lines):
for line in lines:
if "ERROR" in line:
yield line
errors = filter_errors(read_lines("app.log"))
for e in errors:
print(e)
🌀 Бесконечные источники
import time
def ticker():
while True:
yield time.time()
time.sleep(1)
for t in ticker():
print(t)
📑 Вложенные пайплайны с
yield fromdef split_words(lines):
for line in lines:
yield from line.split()
data = ["hello world", "big data stream"]
for word in split_words(data):
print(word)
yield from красиво разрывает вложенные итерации без двойных циклов.from itertools import islice
def stream():
for i in range(1000000):
yield i
print(list(islice(stream(), 5)))
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1👍1
__call__ и __getitem__ — красиво, читаемо, PythonicХочешь, чтобы твой код выглядел как язык сам по себе?
Python позволяет строить мини-языки внутри кода — без библиотек, просто на магии dunder-методов.
__call__Он делает объект вызываемым, как функцию:
class Hello:
def __call__(self, name):
return f"Привет, {name}!"
h = Hello()
print(h("Мир")) # Привет, Мир!
Полезно, когда хочется сделать объекты лаконичными. Можно даже прокидывать параметры как в обычной функции.
__getitem__Позволяет обращаться к объекту через квадратные скобки:
class SecretBox:
def __getitem__(self, key):
return f"🔒 Внутри: {key}"
box = SecretBox()
print(box["password"]) # 🔒 Внутри: password
Ты можешь использовать это для красивого синтаксиса без точек и скобок.
class SQL:
def __call__(self, table):
return f"SELECT * FROM {table}"
def __getitem__(self, field):
return f'"{field}"'
q = SQL()
print(q("users")) # SELECT * FROM users
print(q["email"]) # "email"
Теперь у нас объект, который и вызывает таблицу, и красиво оформляет поля.
class API:
def __init__(self, base):
self.base = base
def __getitem__(self, endpoint):
return f"{self.base}/{endpoint}"
def __call__(self, endpoint, **params):
query = "&".join(f"{k}={v}" for k, v in params.items())
return f"{self.base}/{endpoint}?{query}"
api = API("https://api.server")
print(api["users"]) # https://api.server/users
print(api("users", id=5, active=True)) # https://api.server/users?id=5&active=True
Такой подход часто используют в клиентах к REST API, чтобы код выглядел как естественный DSL.
class K:
def __getitem__(self, key):
return lambda d: d.get(key)
k = K()
data = {"name": "Neo", "role": "Hacker"}
print(k["name"](data)) # Neo
print(k["role"](data)) # Hacker
Так строятся мини-функции доступа к полям. Особенно круто в map/filter.
__call__ и __getitem__ — это способ превратить скучные объекты в выразительный синтаксис. Так строятся мини-языки прямо в Python: понятные, компактные и без лишнего шума.Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
functools.cache вместо самодельного мемо — просто и эффективноЗачем писать свои кеши, если Python уже умеет это из коробки?
С версии 3.9 появился
@cache — аналог lru_cache, но проще и быстрее, если тебе не нужен лимит._cache = {}
def fib(n):
if n in _cache:
return _cache[n]
if n <= 1:
return n
_cache[n] = fib(n-1) + fib(n-2)
return _cache[n]from functools import cache
@cache
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
@lru_cachefrom functools import lru_cache, cache
@lru_cache: можно задать размер (maxsize), есть контроль@cache: просто бесконечный кеш всех входов → выходовЕсли не нужна "очистка", бери
@cache — он быстрее и проще.from functools import cache
import requests
@cache
def get_user_info(user_id):
print("Запрос к API...")
return requests.get(f"https://api.example.com/users/{user_id}").json()
get_user_info(42) # Выполнит запрос
get_user_info(42) # Возьмёт из кеша
@functools.cache — это твой встроенный кеш без лишнего кода.Хорош для любых чистых функций, где важна скорость и повторяемость.
Не изобретай велосипед — используй
cache.Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍3
Даже опытные разработчики пишут код, который потом сложно читать, отлаживать и сопровождать. Вот 5 антипаттернов, которые встречаются особенно часто — и что с ними делать.
1. Мутируемые значения по умолчанию
def append_item(item, items=[]):
items.append(item)
return items
Ошибка —
items сохраняет состояние между вызовами. Итог — неожиданные данные.def append_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
2. Импорт всего (`from module import *`)
Так ты теряешь контроль над namespace и рискуешь конфликтами имён. Особенно опасно в больших проектах.
from math import sqrt, pi
3. Голый `except:`
try:
do_something()
except:
pass
Так можно «поймать» даже
KeyboardInterrupt, и программа зависнет.try:
do_something()
except ValueError:
handle()
4. Чрезмерно вложенные list comprehensions
[x for x in lst if x > 0 if x < 100 if x % 2 == 0]
Читаемость? Нулевая.
result = []
for x in lst:
if 0 < x < 100 and x % 2 == 0:
result.append(x)
5. Копирование ссылок на изменяемые объекты
data = [{}] * 3
data[0]["a"] = 1
print(data)Ожидаешь три разных словаря? А получил три ссылки на один и тот же.
data = [{} for _ in range(3)]Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤2
Метод списка
.sort() и встроенная функция sorted() принимают параметр key. Им должен быть вызываемый объект, который принимает очередной элемент (в нашем случае словарь) и возвращает значение-критерий сортировки.Код ниже показывает, как отсортировать список людей по возрасту:
users = [{'age': 30}, {'age': 20}, {'age': 10}]
users.sort(key=lambda user: user['age'])
>>> [{'age': 10}, {'age': 20}, {'age': 30}]
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1
Иногда ты не знаешь заранее, сколько аргументов получит твоя функция. Python даёт тебе две магические конструкции для таких случаев —
*args и **kwargs.*args собирает все позиционные аргументы в кортеж:def add_all(*args):
return sum(args)
add_all(1, 2, 3) # ➡️ 6
**kwargs собирает все именованные аргументы в словарь:def print_config(**kwargs):
for k, v in kwargs.items():
print(f"{k} = {v}")
print_config(debug=True, timeout=10)
# ➡️ debug = True
# ➡️ timeout = 10
def func(a, b, *args, **kwargs):
pass
a и b — обязательные, args — остальные позиционные, kwargs — именованные.1. Проксирование аргументов
Ты вызываешь другую функцию, не зная её сигнатуру:
def wrapper(*args, **kwargs):
return real_func(*args, **kwargs)
2. Гибкий API без переписывания сигнатур
Вместо:
def create_user(name, email, age, is_admin=False):
...
Можно:
def create_user(**kwargs):
...
3. Расширение без поломки интерфейса
У тебя есть базовый метод:
def process(data, **kwargs):
...
*args и **kwargs — это про гибкость и масштабируемость. Главное — использовать их осознанно: когда действительно нужно, а не “на всякий случай”.Декораторы, middleware, логгеры, фреймворки (Django, Flask), адаптеры, кастомные API, генераторы CLI-интерфейсов.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2💯1
collections.Counter?Counter — это удобный инструмент для подсчета элементов в коллекции.Не обязательно писать цикл и вручную считать каждый элемент 👇
from collections import Counter
data = "aabbbcccc"
counter = Counter(data)
print(counter) # Counter({'c': 4, 'b': 3, 'a': 2})
print(counter.most_common(2)) # [('c', 4), ('b', 3)]
words = ["apple", "banana", "apple", "orange", "banana", "banana"]
print(Counter(words)) # {'banana': 3, 'apple': 2, 'orange': 1}
🔥
Counter особенно полезен для:— анализа текста
— подсчёта слов
— статистики
— обработки данных
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1
В Python для этого не нужен цикл 👇
text = "Python! "
print(text * 3)
👉 Результат:
Python! Python! Python!
💡 То же самое работает со списками:
nums = [1, 2]
print(nums * 3)
Получим:
[1, 2, 1, 2, 1, 2]
🔥 Это удобно, когда нужно быстро создать повторяющуюся строку или последовательность.
⚠️ Но со списками есть нюанс: элементы не копируются глубоко - для вложенных изменяемых объектов это может привести к неожиданному поведению.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1
Можно заставить Python буквально говорить 🔊
Для этого есть библиотека
pyttsx3:
import pyttsx3
engine = pyttsx3.init()
engine.say("Привет! Я говорю с помощью Python")
engine.runAndWait()
📦 Установка:
pip install pyttsx3
💡 Можно сделать функцию:
def speak(text):
engine.say(text)
engine.runAndWait()
speak("Сегодня изучаем Python!")
🔥 На этой основе можно сделать:
— голосового помощника
— программу для чтения текста
— озвучку уведомлений
— простого desktop-бота
И всё это — без сложных моделей и API 🚀
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍2
Нужно быстро достать текст из PDF? Можно автоматизировать это 👇
import fitz
doc = fitz.open("document.pdf")
for page in doc:
print(page.get_text())
📦 Установка:
pip install pymupdf
💡 А если нужно сохранить весь текст в файл:
with open("result.txt", "w", encoding="utf-8") as file:
for page in doc:
file.write(page.get_text())
🔥 Получается простой конвертер PDF → TXT.
На этом можно построить:
— поиск по документам
— обработку большого количества PDF
— извлечение данных из отчётов
— подготовку текста для AI
⚠️ Если PDF состоит из сканов, обычное извлечение текста может не сработать - тогда понадобится OCR.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2❤1
Вайбкодим «Полный путь разработки AI-агентов: от MCP до Agentic RAG»
Образовательная программа на Stepik по разработке современных AI-агентов.
Начинаем с Model Context Protocol: создаём MCP-серверы, подключаем инструменты и внешние API, а затем переходим к разработке Agentic RAG-систем с памятью, рассуждением, поиском знаний и оценкой качества.
• Model Context Protocol и MCP-серверы
• LangChain, LangGraph, LlamaIndex
• API, инструменты, ресурсы и промпты
• Классический RAG → Agentic RAG
• ReAct, память и планирование
• Графы знаний и свои инструменты
• Трассировка и подготовка к production
Успей, скидка 20% действует 48 часов
↗️ Начать обучение на Stepik
Образовательная программа на Stepik по разработке современных AI-агентов.
Начинаем с Model Context Protocol: создаём MCP-серверы, подключаем инструменты и внешние API, а затем переходим к разработке Agentic RAG-систем с памятью, рассуждением, поиском знаний и оценкой качества.
• Model Context Protocol и MCP-серверы
• LangChain, LangGraph, LlamaIndex
• API, инструменты, ресурсы и промпты
• Классический RAG → Agentic RAG
• ReAct, память и планирование
• Графы знаний и свои инструменты
• Трассировка и подготовка к production
Успей, скидка 20% действует 48 часов
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
Например, в папке лежат сотни файлов, и нужно понять, какие из них полностью одинаковые.
Можно сравнивать их по хэшу 👇
from pathlib import Path
import hashlib
def get_hash(path):
return hashlib.md5(path.read_bytes()).hexdigest()
files = {}
for file in Path("files").iterdir():
if file.is_file():
file_hash = get_hash(file)
files.setdefault(file_hash, []).append(file)
for group in files.values():
if len(group) > 1:
print("Дубликаты:")
for file in group:
print(" ", file)
💡 Идея простая:
📄 файл → 🔢 хэш → 🔍 сравниваем хэши
Если хэши совпали, файлы с высокой вероятностью одинаковые.
🔥 Такой скрипт можно использовать для:
— очистки папок
— поиска повторяющихся фото
— анализа больших архивов
— автоматизации работы с файлами
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1
«Освоение модульного тестирования с использованием Pytest» курс на Stepik
Сегодня умение писать тесты ценится почти так же, как и умение писать сам код. Если pytest, fixtures, CI/CD и coverage всё ещё вызывают вопросы самое время это исправить
Программа курса:
• Pytest: от базовых тестов до CI/CD
• fixtures, mocking, parametrization
• Flask/API testing
• Selenium и UI тестирование
• Docker + Docker Compose
• GitHub Actions
• coverage и отчёты
• debugging и refactoring тестов
Курс построен вокруг практики: много примеров, готовых кейсов и разборов рабочих сценариев
48 часов действует скидка 25%
↗️ Пройти курс на Stepik
Сегодня умение писать тесты ценится почти так же, как и умение писать сам код. Если pytest, fixtures, CI/CD и coverage всё ещё вызывают вопросы самое время это исправить
Программа курса:
• Pytest: от базовых тестов до CI/CD
• fixtures, mocking, parametrization
• Flask/API testing
• Selenium и UI тестирование
• Docker + Docker Compose
• GitHub Actions
• coverage и отчёты
• debugging и refactoring тестов
Курс построен вокруг практики: много примеров, готовых кейсов и разборов рабочих сценариев
48 часов действует скидка 25%
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3