Списки – это изменяемые последовательности, обычно используемые для хранения однотипных данных (хотя Python не запрещает хранить в них данные разных типов). Представлены классом
list.Кортежи – это неизменяемые последовательности, обычно используемые, чтобы хранить разнотипные данные. Представлены классом
tuple.На уровне языка отличаются тем, что в кортеж нельзя добавить или убрать элемент. На уровне интерпретатора различий нет. Обе коллекции представлены массивом указателей на структуру
PyObject.Существуют специальные функции для работы со списками. Они вызываются методами списка. Ниже приведены наиболее часто используемые:
# Создаем исходный список
lst = [1, 2, 3]
# append(x): добавляет элемент в конец списка
lst.append(4)
# Теперь lst = [1, 2, 3, 4]
# extend(iterable): расширяет список, добавляя элементы из итерируемого объекта
lst.extend([5, 6])
# Теперь lst = [1, 2, 3, 4, 5, 6]
# insert(i, x): вставляет элемент x на позицию i
lst.insert(0, 'start')
# Теперь lst = ['start', 1, 2, 3, 4, 5, 6]
# remove(x): удаляет первое вхождение элемента x
lst.remove(3)
# Теперь lst = ['start', 1, 2, 4, 5, 6]
# pop([i]): удаляет и возвращает элемент на позиции i (по умолчанию последний)
last = lst.pop()
# last = 6, а lst = ['start', 1, 2, 4, 5]
# sort(): сортирует список на месте
lst = [3, 1, 4, 1, 5, 9, 2]
lst.sort()
# Теперь lst = [1, 1, 2, 3, 4, 5, 9]
# reverse(): разворачивает список на месте
lst.reverse()
# Теперь lst = [9, 5, 4, 3, 2, 1, 1]
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍2
В Python можно передать список как отдельные аргументы с помощью
* 👇
def add(a, b, c):
print(a + b + c)
nums = [1, 2, 3]
add(*nums)
👉 Результат:
6
add(*nums)
Python превращает это в:
add(1, 2, 3)
📌 Работает и с кортежами:
coords = (10, 20)
print(*coords)
🔥 Очень полезно при:
— работе с функциями
— динамических данных
— распаковке API-ответов
🚀
*args и распаковка - одна из самых удобных фишек Python#python #фишки
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤2
enumerate() добавляет счётчик к итерации.fruits = ["🍎", "🍌", "🍇"]
for i, fruit in enumerate(fruits):
print(i, fruit)
0 🍎
1 🍌
2 🍇
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤1
Иногда нужно быстро создать QR‑код: для ссылки, текста, визитки или даже Wi‑Fi‑пароля. Python справляется с этим буквально за пару строк.
Давай разберём, как это делается и что внутри происходит.
pip install qrcode[pil]
qrcode. В скобках [pil] — сразу тянет Pillow для работы с изображениями.import qrcode
img = qrcode.make("https://example.com")
img.save("qrcode.png")
make сразу создаёт изображение, которое можно сохранить или отдать пользователю.Обычно хочется чуть больше контроля: размер, плотность, уровень защиты от ошибок, цвета.
Для этого создаём QR‑код «вручную»:
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data("Hello, QR!")
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img.save("custom_qr.png")
version — размер сетки (1 — минимальный);error_correction — сколько данных можно потерять и всё ещё прочитать код;box_size и border — масштаб и рамку.А если хочется добавить логотип или иконку — тоже просто:
Создаём QR‑код с высоким уровнем коррекции ошибок и «вклеиваем» логотип по центру:
from PIL import Image
logo = Image.open("logo.png")
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H)
qr.add_data("https://mysite.com")
qr.make(fit=True)
qr_img = qr.make_image(fill_color="black", back_color="white").convert("RGB")
pos = (
(qr_img.size[0] - logo.size[0]) // 2,
(qr_img.size[1] - logo.size[1]) // 2,
)
qr_img.paste(logo, pos)
qr_img.save("qr_with_logo.png")
Высокий уровень коррекции (
ERROR_CORRECT_H) позволяет восстанавливать данные даже при частичном перекрытии.Просто текст → картинка → готово.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7🔥2
Каждый Python-разработчик использует
pip, но далеко не все понимают, насколько критична дисциплина в управлении зависимостями. Ошибки здесь = боль на проде, в CI и при сборке.pip install some-library # ❌
python -m venv venv
source venv/bin/activate
pip install some-library
Если ты не фиксируешь версии — ты играешь в лотерею.
pip install requests
Через месяц выйдет новая мажорная версия — и твой код может отвалиться.
pip install requests==2.31.0
pip freeze > requirements.txt
pip install -U some-lib # по умолчанию может подтянуть несовместимости
--upgrade-strategy only-if-needed, если хочешь избегать каскадных обновлений.Иногда
pip старый, и ты получаешь ошибки вроде "metadata generation failed".pip install -U pip
pip install pip-tools
Работает как
pip freeze, но человечно:# requirements.in
requests==2.31.0
flask>=2.0
pip-compile requirements.in # генерирует requirements.txt с зависимостями
pip-tools следит за всем остальным.Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤2👏1
yield и зачем он реально нужен в Pythonyield — это не return. Он не завершает функцию, а паузит её и отдаёт результат по частям.
def gen():
yield 1
yield 2
yield 3
for x in gen():
print(x)
1, 2, 3 — не сразу, а по одному.Функция работает до первого `yield`, потом «засыпает» и продолжает дальше.
🧾 Чтение большого файла без нагрузки
def read_lines(path):
with open(path) as f:
for line in f:
yield line.strip()
for line in read_lines("huge.log"):
print(line)
Если файл весит 10 ГБ — всё равно работает.
Никаких
readlines(), которые жрут память.
def counter():
n = 0
while True:
yield n
n += 1
for i in counter():
if i > 5:
break
print(i)
yield тут не обойтись.Можно генерировать бесконечный поток, но обрабатывать его как обычный список.
def even_only(numbers):
for n in numbers:
if n % 2 == 0:
yield n
nums = [1, 2, 3, 4, 5, 6]
for x in even_only(nums):
print(x)
filter(), а свой контролируемый фильтр. Гибко и понятно.yield — это когда ты генерируешь данные на лету.Ты не ждёшь весь список, не держишь всё в памяти — просто отдаёшь, когда нужно.
Нужен потоковый режим?
yield — лучший друг.Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤1🤔1
В Python у тебя есть мощный способ скрыть логику за обычным доступом к атрибутам — и имя ему
@property.Пример:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def area(self):
return math.pi * self._radius ** 2
Теперь:
c = Circle(3)
print(c.area) # 28.27...
area выглядит как поле, но за ним — код. Удобно, безопасно и читаемо.1. Когда значение вычисляется "на лету"
@property
def is_active(self):
return self.last_seen > datetime.now() - timedelta(minutes=5)
user.is_active читается как флаг, но всегда актуален.2. Когда хочешь валидировать данные при установке
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError("Возраст не может быть отрицательным")
self._age = value
person.age = -1 — но это вызовет ошибку, как и должно.3. Когда хочешь спрятать внутренности, не меняя интерфейс
Сначала у тебя было:
user.is_admin # булево поле
А потом ты передумал и решил вычислять его по ролям:
@property
def is_admin(self):
return "admin" in self.roles
user.is_admin останутся рабочими, но теперь они динамичные.@property делает твой код гибким: интерфейс остаётся, а реализацию можно менять. Это инкапсуляция, но по-питоновски.Ты можешь добавить к setter'у ещё и deleter:
@x.deleter
def x(self):
del self._x
Но чаще всего нужны только
getter и setter.@property — это не украшение. Это способ скрыть логику, проверки, кэширование, не портя интерфейс.Ты получаешь читаемый API, не жертвуя гибкостью.
ORM, модели, бизнес-логика, интерфейсы SDK, динамические флаги, защитные проверки. Везде, где важно: "пусть выглядит просто, но работает как надо".
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
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