Python для начинающих
1.06K subscribers
292 photos
3 videos
232 files
61 links
Python для начинающих
Download Telegram
Привет! С вами Иван. Сегодня разберём одну из самых закулисных и магических тем Python — управление памятью: подсчёт ссылок и сборку мусора. Если вы когда-нибудь задумывались, почему Python не просит у вас явно освобождать память, то этот пост для вас!

## Подсчёт ссылок: сколько “рук” держат объект?

Python использует reference counting — счётчик ссылок на каждый объект. Когда переменная указывает на объект, счётчик увеличивается. Как только ни одна переменная на него больше не ссылается, объект считается “ненужным” и подлежит удалению.

Пример:

import sys

a = []
print(sys.getrefcount(a)) # Обычно вернёт 2 (a и аргумент функции)
b = a
print(sys.getrefcount(a)) # Уже 3 ссылки (a, b, и аргумент)
del b
print(sys.getrefcount(a)) # Снова 2


Магия проста: пока есть хотя бы одна “рука”, держащая объект, он живёт. Как только все переменные и ссылки исчезают — память освобождается.

## Сборка мусора: борьба с “каруселями”

Но что, если объекты ссылаются друг на друга? Простое удаление переменных не сработает — каждый будет держать “за руку” другого, и счётчики не опустятся до нуля! Тут появляется garbage collector.

В Python модуль gc следит за такими “каруселями”:

import gc

class Node:
def __init__(self):
self.ref = None

x = Node()
y = Node()
x.ref = y
y.ref = x

del x, y # Ссылок на объекты нет, но карусель всё ещё существует
gc.collect() # Явно собираем мусор, объекты будут удалены


Garbage collector периодически сканирует объекты, чтобы найти непростые циклы, и удаляет их, когда доступ к ним невозможен.

## Практические советы

- Не бойтесь “загрязнить” память — Python делает всё сам.
- Если работаете с большим количеством данных и видите рост памяти — попробуйте вызвать gc.collect().
- Используйте weakref, если нужен временный “тонкий” доступ без увеличения счётчика ссылок.

Пусть ваша память всегда будет свободна для новых идей!
👍2
Как переназначить поток вывода и ввода в Python
Привет! На связи Иван, и сегодня в нашем блоге разберём магию перенаправления потоков ввода и вывода в Python. Зачем это нужно? Например, если вы хотите записывать вывод вашей программы не только на экран, но и в файл, или "обмануть" функцию, которая традиционно ждёт пользовательский ввод, подсовывая ей данные автоматически. Готовы? Погнали!

## Перенаправляем stdout: вывод в файл и обратно

Для работы с потоками в Python служит модуль sys. Самый популярный поток — это sys.stdout, который по умолчанию пишет в консоль. Давайте заставим его писать в файл:

import sys

with open('output.txt', 'w') as file:
original_stdout = sys.stdout # сохраняем ссылку на стандартный поток
sys.stdout = file # перенаправляем stdout в файл
print("Hello, file!")
sys.stdout = original_stdout # возвращаем stdout обратно

print("Hello, console!")

Теперь строка "Hello, file!" попадёт в файл, а "Hello, console!" — как обычно, в терминал.

## Подменяем stdin: автоматизируем ввод

Точно так же можно подменить поток ввода. Это удобно, когда нужно тестировать функции, требующие input от пользователя.

import sys
from io import StringIO

test_input = StringIO('42\n')
original_stdin = sys.stdin
sys.stdin = test_input

number = int(input("Enter a number: "))
print("Number x2:", number * 2)

sys.stdin = original_stdin

В этом примере input не ждёт ввода, а сразу получает '42'. Удобно для юнит-тестов!

## А если хочется перехватить вывод программы?

Трюк с подменой stdout особенно крут, когда хочется легко перехватывать и анализировать результат работы каких-то функций. Например, сделаем функцию, возвращающую всё, что она напечатала:

def capture_output(func):
import sys
from io import StringIO

output = StringIO()
original_stdout = sys.stdout
sys.stdout = output
try:
func()
finally:
sys.stdout = original_stdout
return output.getvalue()

def hello():
print("Hello from function!")

text = capture_output(hello)
print("Captured:", text)

Теперь мы можем обработать результат как строку — не круто ли?

На этом всё! Не стесняйтесь экспериментировать с потоками: это ключ к автоматизации, тестированию и оптимизации ваших программ. Желаю отличной практики и новых открытий с Python!
Иван.
2
Работа с метаклассами: расширенное использование классов
Привет! С вами Иван, сегодня копнем чуточку глубже — поговорим о метаклассах в Python.

Многие слышали про метаклассы как о чем-то мистическом, но на деле — это просто классы, которые создают другие классы. Да-да, в Python даже классы — это объекты, и за тем, как они создаются, следит метакласс. В обычной жизни мы редко сталкиваемся с ними, но если нужно гибко и аккуратно управлять поведением всех классов в проекте — метаклассы становятся отличным инструментом.

## Для чего нужны метаклассы?

С их помощью можно:
- Автоматически модифицировать классы при создании (например, добавлять методы или атрибуты);
- Навязывать единый стиль (например, проверять именование или структуру классов);
- Реализовывать паттерны, вроде Singleton, прямо “на уровне класса”.

## Базовый пример

Создадим метакласс, который автоматически добавляет атрибут category = 'custom_class' во все классы:

class BaseMeta(type):
def __new__(mcs, name, bases, attrs):
attrs['category'] = 'custom_class'
return super().__new__(mcs, name, bases, attrs)

class ExampleClass(metaclass=BaseMeta):
pass

print(ExampleClass.category) # custom_class


## Валидация структуры класса

Иногда хочется убедиться, что каждый класс имеет нужные методы:

class MethodCheckerMeta(type):
def __new__(mcs, name, bases, attrs):
if 'process' not in attrs:
raise TypeError('Class must define "process" method')
return super().__new__(mcs, name, bases, attrs)

class DataWorker(metaclass=MethodCheckerMeta):
def process(self):
print("Processing...")

# class BadWorker(metaclass=MethodCheckerMeta):
# pass # Ошибка при определении класса!


## Подмена методов на лету

Допустим, хотим, чтобы все методы начинали логировать своё вызовы:

def log_decorator(func):
def wrapper(*args, **kwargs):
print(f'Call {func.__name__}')
return func(*args, **kwargs)
return wrapper

class LoggerMeta(type):
def __new__(mcs, name, bases, attrs):
for key, val in attrs.items():
if callable(val):
attrs[key] = log_decorator(val)
return super().__new__(mcs, name, bases, attrs)

class UserAction(metaclass=LoggerMeta):
def update_profile(self):
print("Profile updated")

u = UserAction()
u.update_profile()


Как видите, метаклассы — секретная дверца Python к по-настоящему мощным и гибким фичам. Пользуются ими редко, но знать о них точно стоит!
👍2
Как работает itertools: от простых комбинаций до сложных перестановок
Привет, друзья! На связи Иван, и сегодня мы окунемся в мир Python-модуля, который любит любой ценитель изящного кода — речь о itertools. Этот модуль открыт как сундук с сокровищами для разных комбинаторных задач: от банального перебора вариантов до создания хитроумных перестановок.

## Зачем вообще нужен itertools?

Когда возникает задача "перебрать все комбинации", "замиксовать значения" или "иcкать все варианты", большинство новичков берёт в руки вложенные циклы и — устаёт. Но если вы знаете про itertools, эти задачи превращаются в пару строк элегантного кода.

Давайте разбираться с основами!

### 1. Комбинации

Выберите любые 2 книги из 4-х? За нас всё сделает itertools.combinations:

from itertools import combinations

books = ['The Hobbit', 'Python 101', 'Moby Dick', 'War and Peace']
for combo in combinations(books, 2):
print(combo)


Этот код выведет все возможные пары книг — и ни одна не повторится.

### 2. Перестановки

Все возможные варианты порядка трёх футболистов на пьедестале? Простая задача для permutations:

from itertools import permutations

players = ['Alice', 'Bob', 'Charlie']
for p in permutations(players):
print(p)


Здесь будет 6 вариантов (3!) — идеально для задач, где важен порядок.

### 3. Произведения (Декартово произведение)

Когда нужно смиксовать элементы из разных коллекций, используем product:

from itertools import product

colors = ['red', 'green']
sizes = ['S', 'M']
for item in product(colors, sizes):
print(item)


Ты быстро получаешь комбинации вида: ('red', 'S'), ('red', 'M'), …

### 4. Комбинации с повторениями

Допустим, нужно узнать, какие мороженое можно собрать из 3-х шариков, если вкусы могут повторяться:

from itertools import combinations_with_replacement

flavors = ['chocolate', 'vanilla', 'strawberry']
for combo in combinations_with_replacement(flavors, 3):
print(combo)


Получаем все возможные варианты с повторениями, без головной боли!

---

itertools — это как швейцарский нож для работы с итерациями. Освоив всего несколько функций, ты вытесняешь громоздкие циклы лаконичными генераторами. Заходи в документацию и пробуй новые инструменты — и Python станет для тебя ещё более мощным и гибким!
👍1
Создание и управление потоками с использованием threading
Привет, друзья! С вами Иван, и сегодня мы погрузимся в захватывающий мир многопоточности на Python с помощью модуля threading. Даже если вы пока не запускали код "параллельно", этот пост для вас!

### Что такое потоки и зачем они нужны?

Обычно Python выполняет код последовательно. Но что если вам нужно скачивать картинки, опрашивать API и тут же обновлять интерфейс? Здесь на сцену выходит threading — стандартный модуль для работы с потоками. Потоки (threads) позволяют запускать функции параллельно, не ожидая завершения одной, чтобы стартовать другую.

### Простой пример: запуск двух задач одновременно

Посмотрим, как это выглядит на практике:

import threading
import time

def print_numbers():
for i in range(5):
print(f"Number: {i}")
time.sleep(1)

def print_letters():
for letter in 'abcde':
print(f"Letter: {letter}")
time.sleep(1)

t1 = threading.Thread(target=print_numbers)
t2 = threading.Thread(target=print_letters)

t1.start()
t2.start()

t1.join()
t2.join()
print("Done!")


Две функции работают параллельно: одна выводит числа, другая — буквы.

### Как управлять потоками?

- Создание: каждый поток — это экземпляр threading.Thread, который принимает функцию через параметр target, и ее аргументы через args.
- Запуск: .start() начинает выполнение в новом потоке.
- Ожидание завершения: .join() блокирует основной поток, пока дочерний не закончится.

### А если нужно знать, работает ли поток?

worker = threading.Thread(target=time.sleep, args=(3,))
worker.start()
print(worker.is_alive()) # True
worker.join()
print(worker.is_alive()) # False


Метод is_alive() вернет True, пока поток еще трудится.

### Безопасность данных — используем Lock

Потоки могут обращаться к одним и тем же данным, поэтому важно использовать блокировки (Lock):

counter = 0
lock = threading.Lock()

def increment():
global counter
for _ in range(1000):
with lock:
counter += 1

threads = [threading.Thread(target=increment) for _ in range(10)]
for t in threads: t.start()
for t in threads: t.join()
print(counter) # Всегда 10000!


Lock гарантирует, что только один поток изменяет переменную в конкретный момент.

---

threading открывает путь к одновременному исполнению кода и ускоряет задачи ввода-вывода. Главное — не забывайте про безопасный доступ к общим данным, и тогда ваши приложения станут гораздо мощнее!
👍1
Использование модели повторяющихся задач с schedule
## Schedule — твой персональный дирижёр в мире повторяющихся задач

Всем привет! С вами Иван, и сегодня мы поговорим о настоящей находке для каждого, кто устал вручную запускать одни и те же функции в Python. Представляю вам модуль schedule — отличный инструмент для простого управления повторяющимися задачами.

### Что это такое?

Модуль schedule позволяет легко и понятно запускать ваши Python-функции по расписанию: каждый день, каждый час, по выходным или хоть каждую пятую минуту. На вид — как мини-крон прямо внутри кода, только гораздо дружелюбнее.

### Установка

Перед стартом — минутка магии в консоли:

pip install schedule


### Примеры использования

Разберём на пальцах. Допустим, вы хотите, чтобы скрипт отправлял напоминание выпить воды каждый час. Вот как это делается:

import schedule
import time

def remind_water():
print("Time to drink some water!")

schedule.every(1).hour.do(remind_water)

while True:
schedule.run_pending()
time.sleep(1)


Теперь функция remind_water будет срабатывать раз в час, пока скрипт работает.

А если нужно выполнять задачу только по рабочим дням — не проблема!

def check_email():
print("Don't forget to check your email!")

schedule.every().monday.do(check_email)
schedule.every().tuesday.do(check_email)
schedule.every().wednesday.do(check_email)
schedule.every().thursday.do(check_email)
schedule.every().friday.do(check_email)


Можно и покреативить: запускать задачу каждые 10 минут или, скажем, в определённое время суток:

def backup_files():
print("Backup started!")

schedule.every(10).minutes.do(backup_files)
schedule.every().day.at("23:00").do(backup_files)


### Фишки модуля

- schedule.run_pending() проверяет — не пора ли что запустить, и делает это.
- Время можно указывать сразу для нескольких задач с разной периодичностью.
- Работает просто: без лишних настроек, конфигов и заморочек.

### Минусы и ограничения

Модуль не умеет работать в фоне — если закроете скрипт, задачи выполняться не будут. Для продвинутых сценариев (например, для серверов) нужны другие решения, но для учебных и простых домашних проектов schedule — настоящее спасение.

Практикуйте и экспериментируйте, ведь расписание — ключ к продуктивности даже в программировании!
Как настроить локальный сервер с использованием модуля wsgiref
Как настроить локальный сервер с использованием модуля wsgiref
Привет, меня зовут Иван, и сегодня я расскажу тебе, как устроить себе настоящий мини-сервер ― буквально в пару строк! Если ты только начинаешь своё путешествие в мир Python и веба, модуль wsgiref — это твой пропуск на этот интересный уровень.

### Что такое WSGI и зачем он нужен?

WSGI (Web Server Gateway Interface) — это стандарт, который помогает Python-программам общаться с веб-серверами. Встроенный модуль Python wsgiref — это как виртуальный щит, который превращает твой код в простой веб-сервер. Он отлично подходит для тестов и экспериментов — идеально для начинающих!

### Первый пример: “Hello, world!”

Создадим файл server.py:

from wsgiref.simple_server import make_server

def simple_app(environ, start_response):
status = '200 OK'
headers = [('Content-type', 'text/plain; charset=utf-8')]
start_response(status, headers)
return [b"Hello, world!"]

if __name__ == '__main__':
server = make_server('localhost', 8080, simple_app)
print("Serving on http://localhost:8080/ ...")
server.serve_forever()


Запускай скрипт, переходи в браузере на http://localhost:8080 — и ты увидишь волшебное “Hello, world!”. Именно так начинается путь любого веб-разработчика.

### Как это работает?

- make_server создает сервер и принимает три параметра: адрес, порт и функцию-приложение.
- simple_app — “основное блюдо”. Она получает две переменные: данные о запросе (environ) и функцию для передачи статуса с заголовками (start_response).
- Возвращаемый список содержит байты, которые отдаются в ответ пользователю.

### А если хочется HTML?

Легко! Вот пример функции, отдающей HTML:

def html_app(environ, start_response):
status = '200 OK'
headers = [('Content-type', 'text/html; charset=utf-8')]
start_response(status, headers)
html = """
<html>
<head><title>WSGI Test</title></head>
<body><h1>Привет из wsgiref!</h1></body>
</html>
"""
return [html.encode('utf-8')]


### Поддержка разных путей

Хочешь разные ответы по разным URL? Используй environ['PATH_INFO']:

def path_app(environ, start_response):
path = environ.get('PATH_INFO', '/')
if path == '/about':
body = "This is the About page."
else:
body = "Home page."
status = '200 OK'
headers = [('Content-type', 'text/plain; charset=utf-8')]
start_response(status, headers)
return [body.encode('utf-8')]


Добавь эту функцию в make_server — и твой сервер стал умнее!

---

Вот так легко можно войти в веб-мир Python, используя стандартные инструменты. Не нужен ни Flask, ни Django, ни танцы с бубном — только чистый Python и твоя фантазия!
👍1