Программистика
Yopiq kanal
Лучший канал про python Ссылка для друга: https://t.me/+Ai6ughKtf5g2ZmFi Купить рекламу: https://telega.in/c/+Ai6ughKtf5g2ZmFi Админ: @JeyRahol По рекламе: @ReivuManager
Ko'proq ko'rsatish5 579
Obunachilar
-524 soatlar
-437 kunlar
-10430 kunlar
Postlar arxiv
5 581
🔑 Секрет `dict.get()`: забудь про `if key in dict`
Новички часто проверяют наличие ключа в словаре, чтобы безопасно прочитать значение:
if 'score' in data:
value = data['score']
else:
value = 0
В Python есть элегантный способ в одну строку:
value = data.get('score', 0)
Что здесь происходит:
get(key, default) пытается вернуть значение по ключу. Если ключа нет — возвращает default (по умолчанию None). Никаких исключений, никаких if.
Где пригодится:
# Счётчик без defaultdict
count = {}
words = ['a', 'b', 'a']
for w in words:
count[w] = count.get(w, 0) + 1 # {'a': 2, 'b': 1}
# Безопасное чтение вложенных данных
user = {'name': 'Алиса'}
age = user.get('age', 'не указан') # 'не указан'
🟢 Почему круто:
Короче, быстрее, читаемее. Встроено в Python, не требует импортов.
Запомни: dict.get() — твой друг при работе со словарями.
Программистика || #tips5 581
💻 Вопрос с собеседования: Чем отличается `@staticmethod` от `@classmethod`?
Оба декоратора используются в классах, и оба не требуют экземпляра для вызова. Но начинающие путают их, а опытные разработчики чётко знают, когда что использовать. Разбираемся раз и навсегда.
📌 Базовый ответ (которого ждут от джуна):
-
@staticmethod — это обычная функция внутри класса. Она не получает ни self, ни cls. Работает как независимая утилита, просто сгруппированная внутри класса для удобства.
- @classmethod — получает первым аргументом класс (cls). Может обращаться к атрибутам класса и создавать экземпляры класса.
class MyClass:
@staticmethod
def static_method(x):
return x * 2
@classmethod
def class_method(cls, x):
return cls() # может создать экземпляр
obj = MyClass()
print(MyClass.static_method(5)) # 10
print(MyClass.class_method(5)) # <__main__.MyClass object at ...>
🧱 Глубокий разбор (для мидла и сеньора):
1. `@staticmethod` — просто функция внутри класса
- Не знает о классе и его атрибутах.
- Не может изменить состояние класса или экземпляра.
- Используется как хелпер, логически связанный с классом.
class MathUtils:
@staticmethod
def is_even(n):
return n % 2 == 0
# Вызов возможен и от класса, и от экземпляра
print(MathUtils.is_even(4)) # True
2. `@classmethod` — работает с классом
- Получает cls — ссылку на сам класс.
- Может читать и менять атрибуты класса.
- Часто используется как альтернативный конструктор (factory method).
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def from_birth_year(cls, name, birth_year):
age = 2025 - birth_year
return cls(name, age) # вызывает __init__
p = Person.from_birth_year("Алиса", 1995)
print(p.age) # 30
⚠️ Частые ошибки и ловушки
1. Вызывать `@staticmethod` через `self` — можно, но бессмысленно, проще через класс.
2. Пытаться изменить атрибут класса через `@staticmethod` — не получится без явного указания имени класса.
3. Забыть про `cls` в `@classmethod` — метод перестанет быть классовым, вы получите обычный метод с первым аргументом, который будет восприниматься как self.
4. Использовать `@staticmethod` там, где нужна логика с классом — тогда лучше @classmethod.
# ❌ Плохо: статический метод, который должен работать с классом
class Counter:
count = 0
@staticmethod
def increment():
Counter.count += 1 # привязка к конкретному классу — негибко
# ✅ Хорошо: классовый метод
class Counter:
count = 0
@classmethod
def increment(cls):
cls.count += 1 # работает с любым наследником
💡 Итоговый ответ для собеседования:
1. @staticmethod — обычная функция, просто лежащая в пространстве имён класса. Не получает ни self, ни cls.
2. @classmethod — получает первым аргументом класс (cls). Может читать/менять атрибуты класса и создавать экземпляры.
3. Главное применение @classmethod — альтернативные конструкторы (например, from_dict, from_string, from_birth_year).
4. @staticmethod используйте для вспомогательных функций, которые логически связаны с классом, но не зависят от его состояния.
5. При наследовании @classmethod будет получать дочерний класс — это даёт гибкость для фабрик.
Запомните: если внутри метода вы используете имя класса жёстко — почти всегда лучше сделать его @classmethod и использовать cls. Код станет более расширяемым.
Программистика || #jobs5 581
Марафон данных: первое знакомство с SQL и Python
Этот курс для тех, кто хочет познакомиться с профессией аналитика данных. Если вы никогда ранее не сталкивались с SQL, Python и продуктовыми метриками, то Data Марафон – для вас! Мы расскажем про самые важные инструменты аналитика данных, и объясним всю суть аналитической работы максимально просто и на реальных примерах.
Чему вы научитесь:
💠Познакомитесь с профессией и примерите на себя роль аналитика данных 💠Узнаете, как выглядят реальные данные и поработаете с ними 💠Изучите и посчитаете основные продуктовые и маркетинговые метрики 💠Научитесь обрабатывать данные с помощью таких инструментов, как SQL и Python 💠Сможете принять решение, развиваться ли Вам в этой профессии
🌐 СсылкаПрограммистика || #Course
5 581
🔥 Мастхев подборка для каждого айтишника
Держите подборку авторских IT-каналов по всем направлениям: курсы, книги, статьи, практики, примеры кода, шпаргалки и прохождение собесов.
▪️ Python ▪️ Java
▪️ Frontend ▪️ C / C++
▪️ ИБ & Хакинг ▪️ DevOps
▪️ Data Science ▪️ Linux
▪️ C# & GameDev ▪️ Курсы IT
▪️ ИИ-бот для IT ▪️ Весь IT
👉🏻 Подписывайся и прокачивай свои навыки
5 581
Алгоритмы и структуры данных с примерами на Python
Цель этой книги – при помощи наглядных иллюстраций и исполняемых примеров кода помочь читателю понять ключевые идеи алгоритмов и структур данных и освоить их воплощение в программном коде. Если вам необходимо подготовиться к собеседованию или получить базовые навыки перед сложным курсом по программированию, но не хватает времени на чтение множества учебников от корки до корки, этот учебник станет для вас спасательным кругом в океане знаний. Книга будет особенно полезна всем, у кого есть начальные навыки программирования, но отсутствует четкое понимание алгоритмов и структур данных. Более опытным читателям она поможет освежить и систематизировать знания об алгоритмах.Программистика || #Книги
5 581
🔄 Безопасный арсенал практических инструкций, курсов и инструментов — только то, что прокачивает навыки.
🥷 OSINT
🤔 Хакинг & ИБ
🖥 Курсы & GitHub
📱 Python
👩💻 Linux & Bash
💼 Фриланс и работа в IT
📱 Общий архив
📂 Книги, курсы, инструкции по ИБ, Python и фриланс на удаленке.
5 581
📦 Лайфхак: `contextlib.suppress` — убираем шумные исключения без `try/except`
Вы когда-нибудь писали такой код, просто чтобы игнорировать ошибку?
try:
os.remove('temp.txt')
except FileNotFoundError:
pass
Это работает, но занимает 4 строки и отвлекает от сути. В Python есть встроенный контекстный менеджер, который делает то же самое в одну строку.
✔️ Как правильно:
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove('temp.txt')
Что здесь происходит:
suppress подавляет указанные исключения внутри блока. Если исключение возникло — оно не прерывает выполнение и не требует обработки. Если не возникло — просто идём дальше.
🧠 Где это реально полезно
1. Закрытие ресурсов, которые могут быть уже закрыты:
with suppress(AttributeError):
conn.close()
2. Попытка удалить временный файл или папку:
with suppress(FileNotFoundError, PermissionError):
shutil.rmtree('temp_dir')
3. Преобразование типов, если не знаешь, получится ли:
with suppress(ValueError):
value = int(maybe_number)
4. Проверка наличия атрибута через getattr:
# вместо if hasattr(obj, 'name'):
with suppress(AttributeError):
name = obj.name
⚠️ Когда НЕ стоит использовать
🔴 Если нужно залогировать ошибку или выполнить действие при её возникновении. 🔴 Если ожидается много разных исключений (лучше явно перечислить нужные). 🔴 В критичных участках, где важна каждая ошибка.💡 Запомни: 🟢
suppress — не замена всем try/except, а инструмент для случаев, когда ошибка ожидаема и неважна.
🟢 Код становится чище и читаемее.
🟢 Импортируется один раз и работает с любым количеством исключений.
Попробуйте заменить свои пустые except: pass на with suppress(...) — и код задышит по-новому.
Программистика || #tips5 581
Программисты, это вам 👇
Держите 5 каналов, которые реально помогают изучать программирование и IT с полного нуля:
🖥 Easy Coder — все направления IT.
👩💻 Easy Python — всё о Python.
🌐 Easy WebDev — Frontend, Backend.
🔠 Easy InfoSec — ИБ, Хакинг.
🖥 Easy GitHub — лучшее с GitHub.
Тонны бесплатной инфы для любого уровня подготовки ✔️
5 581
🌀 Tenacity — это библиотека для автоматических повторных попыток при ошибках. Добавляется одной строкой-декоратором к любой функции. Умеет экспоненциальные задержки, случайные паузы, логирование и остановку по условию. Идеально для ненадёжных API, сетевых запросов и работы с базами данных.
🐱 Ссылка на GitHub
Программистика|| #Репозиторий
5 581
😈 Senior-безопасник создал крутейший канал про ИБ
Благодаря простым картинкам даже новичок поймет основы хакинга и информационной безопасности.
Присоединяйтесь: @infosec
5 581
🎭 `singledispatch`: одна функция — много лиц (перегрузка по типу)
Вы когда-нибудь хотели, чтобы функция вела себя по-разному в зависимости от типа аргумента, но не писать горы
if isinstance()? В Python есть встроенный декоратор functools.singledispatch, который реализует настоящую перегрузку функций — как в C++ или Java, только проще.
❌ Было (куча if-ов и растущее страдание):
def process(data):
if isinstance(data, int):
return data * 2
elif isinstance(data, str):
return data.upper()
elif isinstance(data, list):
return [x * 2 for x in data]
else:
raise TypeError("Не поддерживается")
✔️ Стало (чисто, расширяемо, по-питонячьи):
from functools import singledispatch
@singledispatch
def process(data):
raise TypeError(f"Не поддерживается: {type(data)}")
@process.register(int)
def _(data):
return data * 2
@process.register(str)
def _(data):
return data.upper()
@process.register(list)
def _(data):
return [x * 2 for x in data]
🧱 Как это работает
singledispatch создаёт универсальную функцию, которая диспетчеризирует вызов на специализированную реализацию в зависимости от типа первого аргумента.
🟢 Базовая функция (с @singledispatch) — как default.
🟢 Зарегистрированные функции (с @process.register(тип)) — перегрузки.
🟢 Имена зарегистрированных функций не важны — обычно их называют _.
🧪 Продвинутый пример: работа с коллекциями
from functools import singledispatch
from collections.abc import Iterable
@singledispatch
def pretty_print(obj):
print(f"Объект: {obj}")
@pretty_print.register(int)
def _(n):
print(f"🟢 Число: {n}")
@pretty_print.register(str)
def _(s):
print(f"📝 Строка: {s[:50]}...")
@pretty_print.register(list)
@pretty_print.register(tuple)
@pretty_print.register(set)
def _(seq):
print(f"📦 Коллекция из {len(seq)} элементов")
# Можно регистрировать по абстрактным базовым классам
@pretty_print.register(Iterable)
def _(it):
print(f"🔄 Итератор: {list(it)[:3]}...")
⚡️ Преимущества и ограничения
🟢 Чистая архитектура: Каждый тип обрабатывается в своей функции, без монолитных if-ов. 🟢 Расширяемость: Новый тип добавляется одной строкой @process.register(MyType), не трогая старый код. 🟢 Поддержка наследования: Если зарегистрирован родительский класс, то дочерний автоматически получит ту же реализацию (можно переопределить). 🟢 Работа с абстрактными базовыми классами: register(Iterable) сработает для всех итерируемых типов.🔗 Подробнее в документации Попробуйте заменить очередной длинный
if-elif на singledispatch — и вы почувствуете, как код становится легче и понятнее.
Программистика || #Статья5 581
⚡️Группа хакеров взломала сервера Skillbox, Geekbrains, Skillfactory и ещё 12 онлайн-школ, чтобы выгрузить их курсы в Telegram
Юристы пытаются удалить каналы за Авторские Права🤡 – потому вот актуальные ссылки на архивы:
По школам:
├ Skillbox (1.12 ТБ)
├ Нетология (846 ГБ)
├ SkillFactory (720 ГБ)
├ GeekBrains (934 ГБ)
└ Другие (3.21 ТБ)
По ЯП:
├ Python (1.48 ТБ)
├ SQL (982 ГБ)
├ C++ (590 ГБ)
├ С (318 ГБ)
├ GoLang (290 ГБ)
└ Другие (3.17 ТБ)
Ссылка на общий архив: @schools_hack_arc
5 581
📸 Image Downloader: Скрипт для массового скачивания картинок с любой страницы
Этот скрипт сделает всё за тебя: найдёт все изображения на странице и скачает их в отдельную папку.
👉 Скрипт `image_downloader.py`:
#!/usr/bin/env python3
"""
Скрипт для скачивания всех картинок с веб-страницы.
Использование: python image_downloader.py https://example.com
"""
import os
import sys
import requests
from urllib.parse import urljoin, urlparse
from bs4 import BeautifulSoup
from pathlib import Path
def download_images(url, folder='downloaded_images'):
# Создаём папку для сохранения
Path(folder).mkdir(exist_ok=True)
# Загружаем страницу
headers = {'User-Agent': 'Mozilla/5.0'}
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
except Exception as e:
print(f"❌ Ошибка загрузки страницы: {e}")
return
soup = BeautifulSoup(response.text, 'html.parser')
# Находим все теги img
img_tags = soup.find_all('img')
print(f"🔍 Найдено изображений: {len(img_tags)}")
# Фильтруем и скачиваем
downloaded = 0
for img in img_tags:
img_url = img.get('src')
if not img_url:
continue
# Превращаем относительный URL в абсолютный
img_url = urljoin(url, img_url)
# Пропускаем data: и другие не-http схемы
if not img_url.startswith(('http://', 'https://')):
continue
# Получаем имя файла
parsed = urlparse(img_url)
filename = os.path.basename(parsed.path)
if not filename or '.' not in filename:
filename = f"image_{downloaded}.jpg"
# Защита от дурака: убираем query-параметры
filename = filename.split('?')[0]
save_path = Path(folder) / filename
# Скачиваем
try:
img_data = requests.get(img_url, headers=headers, timeout=10)
img_data.raise_for_status()
with open(save_path, 'wb') as f:
f.write(img_data.content)
print(f"✅ {filename}")
downloaded += 1
except Exception as e:
print(f"❌ Не удалось скачать {img_url}: {e}")
print(f"\n📊 Готово! Скачано {downloaded} изображений в папку {folder}")
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Использование: python image_downloader.py <URL>")
sys.exit(1)
url = sys.argv[1]
download_images(url)
🛠 Установка зависимостей:
pip install requests beautifulsoup4
🚀 Запуск:
python image_downloader.py https://example.com/gallery
➕ Фишки скрипта:
🟢 Автоматически создаёт папку downloaded_images
🟢 Превращает относительные ссылки в абсолютные
🟢 Пропускает data:URI и другие некорректные схемы
🟢 Обрабатывает ошибки при скачивании
🟢 Игнорирует URL с параметрами (обрезает ?...)
💡 Что можно улучшить:
🟢 Добавить поддержку srcset для выбора лучшего качества
🟢 Фильтровать по минимальному размеру
🟢 Сохранять исходную структуру подпапок
🟢 Распараллелить скачивание (threading)
Программистика || #scripts5 581
🖥 Polars — это сверхбыстрая библиотека для обработки данных на Python и Rust, созданная как современная альтернатива Pandas. Она использует все ядра процессора, эффективно работает с памятью и поддерживает ленивые вычисления, что позволяет обрабатывать датасеты, не помещающиеся в оперативную память. Polars идеально подходит для ETL-пайплайнов, анализа больших данных и задач, где скорость критична.
🐱 Ссылка на GitHub
Программистика|| #Репозиторий
5 581
🚨 СРОЧНО: TELEGRAM ЗАБЛОКИРОВАЛИ. ИЛИ НЕТ?🚨
Обещанная блокировка Telegram должна была случиться именно сегодня. Или не должна? Или уже случилась, но мы не заметили? Технические специалисты по всему миру замерли в ожидании: будет ли глобальный сбой, или это очередной «апдейт политики конфиденциальности»?
🟢 Что говорит статистика?
Наши серверы мониторинга показывают: трафик Telegram в РФ не упал. Пакеты идут, соединения не рвутся. Но есть нюанс — некоторые провайдеры начали экспериментальные фильтрации на уровне DPI, но они пока напоминают скорее «проверку связи», чем полноценную блокировку.
🤷♂️ Давайте устроим перекличку — ставьте эмодзи:
👍 — работает, всё летает
⚡️ — не работает, сижу в VPN
💯 — мне всё равно, я в Mах
Программистика
5 581
💻 Вопрос с собеседования: Что такое GIL в Python и как с ним жить?
GIL (Global Interpreter Lock) — одна из самых обсуждаемых и пугающих тем в Python. Многие считают его фатальным недостатком, но на самом деле это осознанное архитектурное решение. На собеседовании хотят услышать не просто «это блокировка», а понимание, как она работает и когда мешает, а когда нет.
📌 Базовый ответ (которого ждут от джуна):
GIL (Global Interpreter Lock) — это глобальная блокировка интерпретатора CPython, которая позволяет выполняться только одному потоку Python за раз. Это означает, что даже если у вас многоядерный процессор, несколько потоков не могут параллельно выполнять Python-код одновременно.
import threading
counter = 0
def increment():
global counter
for _ in range(1000000):
counter += 1
threads = [threading.Thread(target=increment) for _ in range(2)]
for t in threads: t.start()
for t in threads: t.join()
print(counter) # будет около 1 500 000, а не 2 000 000 из-за переключений GIL
GIL упрощает управление памятью и делает CPython однопоточным по выполнению кода, но многопоточным по I/O.
🧱 Глубокий разбор (для мидла и сеньора):
1. Зачем нужен GIL?
🟢 Упрощает управление памятью (подсчёт ссылок thread-safe без сложных блокировок).
🟢 Ускоряет выполнение однопоточного кода (не нужны мелкие блокировки на каждую операцию).
🟢 Облегчает написание C-расширений.
2. Как GIL влияет на многопоточность?
🟢 I/O-bound задачи: потоки отлично работают, потому что при операциях ввода-вывода GIL отпускается.
🟢 CPU-bound задачи: потоки бесполезны (или даже вредны), так как только один поток выполняет код в каждый момент.
# CPU-bound — бесполезно
def heavy_calc():
sum(i**2 for i in range(10_000_000))
threads = [threading.Thread(target=heavy_calc) for _ in range(4)]
# 4 потока будут работать ДОЛЬШЕ, чем 1!
⚠️ Частые ошибки и ловушки
1. Создавать много потоков для CPU-задач и ждать ускорения — ускорения не будет, только оверхед.
2. Не использовать `multiprocessing` для тяжёлых вычислений — терять производительность.
3. Путать GIL с проблемами синхронизации — даже с GIL нужно использовать блокировки для общих данных (например, threading.Lock), потому что потоки могут переключаться в любой момент.
counter = 0
def unsafe():
global counter
for _ in range(100000):
counter += 1 # неатомарно! нужна блокировка
4. Думать, что асинхронность решает все проблемы параллелизма — asyncio подходит только для I/O, не для CPU.
💡 Итоговый ответ для собеседования:
1. GIL — глобальная блокировка интерпретатора, позволяющая только одному потоку Python выполняться в данный момент.
2. Плюсы: упрощает память, ускоряет однопоточный код, облегчает C-расширения.
3. Минусы: ограничивает параллелизм CPU-задач в многопоточности.
4. Как обойти: для CPU — multiprocessing или C-расширения; для I/O — потоки или asyncio.
5. Важно: GIL не мешает I/O-задачам, но бесполезен для вычислений.
6. Главная мысль: GIL — не баг, а фича, с которой нужно уметь работать, выбирая правильный инструмент под задачу.
Удачи на собеседованиях! 🔥🔥
Программистика ||#jobs5 581
🤖 AI Commits: пусть нейросеть пишет осмысленные коммиты за тебя
Ты 100-й раз пишешь
git commit -m "fix", а потом коллеги и ты сам через месяц не понимаете, что именно было исправлено. Гуглить вклад — слишком лень, писать грамотные сообщения — времени жалко. А что, если это сделает нейросеть? Скрипт смотрит на изменения в коде, анализирует их и предлагает понятное сообщение коммита в стиле Conventional Commits.
👉 Скрипт `ai_commit.py`:
#!/usr/bin/env python3
"""
AI Commit — автоматически генерирует сообщение коммита по изменениям в коде.
Требует установленный и запущенный ollama (или можно заменить на API GPT).
"""
import subprocess
import sys
import argparse
import json
# Настройки: выбери модель из ollama
MODEL = "codellama:7b-instruct" # или "llama3.2", "mistral", "qwen2.5-coder"
# Если хочешь использовать OpenAI, раскомментируй и укажи ключ
# USE_OPENAI = False
# OPENAI_API_KEY = "sk-..."
def get_git_diff():
"""Возвращает diff текущих изменений"""
try:
diff = subprocess.check_output(
["git", "diff", "--cached", "--no-color"],
stderr=subprocess.DEVNULL,
text=True
)
if not diff.strip():
# Если нет изменений в staged, берём unstaged
diff = subprocess.check_output(
["git", "diff", "--no-color"],
stderr=subprocess.DEVNULL,
text=True
)
return diff
except subprocess.CalledProcessError:
print("❌ Не удалось получить diff. Вы в git-репозитории?")
sys.exit(1)
def generate_commit_message(diff, use_ollama=True):
"""Отправляет diff модели и получает сообщение коммита"""
prompt = (
"Ты — эксперт по Git. Напиши краткое сообщение коммита (одной строкой) в стиле Conventional Commits, "
"которое описывает следующие изменения в коде. Не добавляй лишних комментариев, только само сообщение.\n\n"
f"Изменения:\n{diff}"
)
if use_ollama:
try:
import ollama
response = ollama.chat(model=MODEL, messages=[{"role": "user", "content": prompt}])
return response['message']['content'].strip()
except Exception as e:
print(f"❌ Ошибка при обращении к ollama: {e}")
print("Убедитесь, что ollama запущен (ollama serve) и модель загружена.")
sys.exit(1)
else:
# Вариант с OpenAI API (раскомментируй при необходимости)
import openai
openai.api_key = OPENAI_API_KEY
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}],
max_tokens=50
)
return response.choices[0].message.content.strip()
def main():
parser = argparse.ArgumentParser(description="Генератор сообщений коммита через AI")
parser.add_argument("-m", "--model", default=MODEL, help="Модель ollama")
parser.add_argument("--openai", action="store_true", help="Использовать OpenAI API вместо ollama")
args = parser.parse_args()
diff = get_git_diff()
if not diff.strip():
print("⚠️ Нет изменений для коммита. Добавьте файлы через git add.")
return
print("🤖 Генерирую сообщение коммита...")
msg = generate_commit_message(diff, use_ollama=not args.openai)
print("\n💡 Предлагаемое сообщение:\n")
print(msg)
print("\n✅ Если подходит, выполните:")
print(f' git commit -m "{msg}"')
print(" Или скопируйте сообщение и отредактируйте.")
if __name__ == "__main__":
main()
🛠 Установка и настройка:
1. Установи скрипт:
pip install ollama # или openai, если будешь использовать API
2. Установи и запусти ollama (бесплатно, локально):
# macOS/Linux
curl -fsSL https://ollama.com/install.sh | sh
# Windows — скачай с сайта
ollama serve &
ollama pull codellama:7b-instruct # или другую модель
3. Сделай скрипт исполняемым и добавь в PATH (опционально):
chmod +x ai_commit.py
sudo ln -s $(pwd)/ai_commit.py /usr/local/bin/ai-commit
Программистика || #scripts
Endi mavjud! Telegram Tadqiqoti 2025 — yilning asosiy insaytlari 
