Использование дескрипторов для управления доступом к атрибутам
Дескрипторы позволяют централизованно управлять доступом, чтением, записью и удалением атрибутов в классах. Это мощный инструмент для создания более гибкого и читаемого кода.
Пример: класс для проверки типов через дескриптор
class Typed:
def __init__(self, name, expected_type):
self.name = name
self.expected_type = expected_type
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__.get(self.name)
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError(f"Ожидаются данные типа {self.expected_type}, получены данные типа {type(value)}")
instance.__dict__[self.name] = value
def __delete__(self, instance):
raise AttributeError("Невозможно удалить атрибут")
class Person:
name = Typed("name", str)
age = Typed("age", int)
def __init__(self, name, age):
self.name = name
self.age = age
try:
p = Person("Алиса", 10)
print(p.name)
p.age = "10"
except TypeError as e:
print(e)
Вывод:
Алиса
Ожидаются данные типа <class 'int'>, получены данные типа <class 'str'>
Упрощение кода классов с помощью dataclass
Использование dataclass
в Python – удобный способ упрощения кода для классов, которые предназначены только для хранения данных. Вместо ручного определения методов __init__
, __repr__
, __eq__
и других, можно автоматически генерировать их, используя аннотации типов. Преимущества использования dataclass
:
- Сокращение кода – нет необходимости вручную писать методы вроде
__init__
,__repr__
, и их поведение не нужно поддерживать. - Читаемость – код легче читать, потому что вы видите только список полей, описывающих объект.
- Гибкость – можно добавлять методы или настройки, чтобы изменять поведение класса.
- Автоматизация – встроенные методы генерируются автоматически, вероятность ошибок сводится к минимуму.
Пример:
from dataclasses import dataclass
@dataclass
class Employee:
name: str
last_name: str
age: int
position: str
emp = Employee(name="Евгений", last_name="Онегин", age=30, position="бэкендер")
print(emp)
emp2 = Employee(name="Пьер", last_name="Безухов", age=30, position="фронтендер")
print(emp == emp2)
print(repr(emp2))
Вывод:
Employee(name='Евгений', last_name='Онегин', age=30, position='бэкендер')
False
Employee(name='Пьер', last_name='Безухов', age=30, position='фронтендер')
Кастомные менеджеры контекста
Когда возможностей стандартного контекстного менеджера (например, open) недостаточно, можно создать свой, чтобы упростить управление специфическими ресурсами.
Пример: собственный контекстный менеджер для работы с базой данных
import sqlite3
from contextlib import contextmanager
from pathlib import Path
@contextmanager
def open_db(db_path):
db_path = Path(db_path)
if not db_path.exists():
raise FileNotFoundError(f"Файл базы данных {db_path} не найден.")
connection = sqlite3.connect(db_path)
try:
yield connection
finally:
connection.commit()
connection.close()
db_file = "example.db"
with open_db(db_file) as conn:
cursor = conn.cursor()
cursor.execute('''CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
age INTEGER)''')
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Евгений", 30))
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Татьяна", 25))
print("Данные внесены в базу.")
Вывод:
Данные внесены в базу.
Аннотации функций
Аннотации функций показывают, какие типы данных ожидаются на входе и какой тип будет возвращен. Они делают код понятнее, безопаснее и удобнее для работы, и особенно полезны в крупных проектах, где требуется строгий контроль типов и ясное описание поведения функций. Например, здесь сразу видно, что функция принимает length
и width
в виде чисел с плавающей точкой float
и возвращает значение того же типа:
def calculate_area(length: float, width: float) -> float:
return length * width
Многие IDE и инструменты проверки типов (например, MyPy) могут использовать аннотации для поиска ошибок в коде еще до его выполнения. Например, если вы случайно передадите строку вместо числа, то получите предупреждение, что строка str
не соответствует ожидаемому типу float
:
area = calculate_area("5.0", 3.2) # Ошибка!
Декораторы для повторного использования кода
Декораторы позволяют отделить дополнительную функциональность (например, логирование, проверку прав доступа, измерение времени выполнения) от основной логики функции. Это делает код более понятным и легким в сопровождении.
Пример: измерение времени выполнения функции
import time
def measure_time(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"Функция {func.__name__} выполнена за {end - start:.4f} секунд")
return result
return wrapper
@measure_time
def compute():
sum(range(10**7))
compute()
Вывод:
Функция compute выполнена за 0.1000 секунд
Использование functools для оптимизации сложных операций с функциями
Модуль functools
– мощный инструмент, который:
- Помогает оптимизировать производительность (за счет кэширования).
- Облегчает создание гибких и модульных функций.
- Упрощает работу с функциями высшего порядка.
Пример: оптимизация производительности с помощью кэширования
from functools import lru_cache
@lru_cache(maxsize=100)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(100))
Без кэширования вычисление больших значений Фибоначчи было бы чрезвычайно медленным из-за экспоненциальной сложности. Но с lru_cache
предыдущие результаты сохраняются, что значительно сокращает время выполнения.
Эффективные структуры данных collections
Модуль collections предоставляет высокоуровневые структуры данных, которые оптимизируют задачи, связанные с обработкой коллекций. Например, defaultdict
автоматически создает значения по умолчанию для отсутствующих ключей, что устраняет необходимость в проверках или дополнительных условиях. А еще он очень удобен для группировки или построения вложенных структур:
from collections import defaultdict
grouped = defaultdict(list)
data = [("фрукты", "яблоко"), ("животное", "кошка"), ("фрукты", "банан"), ("животное", "собака")]
for key, value in data:
grouped[key].append(value)
print(grouped)
Вывод:
defaultdict(<class 'list'>, {'фрукты': ['яблоко', 'банан'], 'животное': ['кошка', 'собака']})
Counter
упрощает подсчет:
from collections import Counter
text = "hello world"
char_count = Counter(text)
print(char_count.most_common(3))
Вывод:
[('l', 3), ('o', 2), ('h', 1)]
namedtuple
упрощает создание неизменяемых объектов с именованными полями:
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p.y)
Вывод:
3 4
Упрощение многопоточности и многопроцессорности
Модуль concurrent.futures
предоставляет высокоуровневый интерфейс для выполнения задач параллельно с использованием:
ThreadPoolExecutor
– для задач ввода-вывода.ProcessPoolExecutor
– для вычислительно сложных задач, нагружающих процессор.
Пример: загрузка нескольких веб-страниц в разных потоках
import requests
from concurrent.futures import ThreadPoolExecutor
def fetch_url(url):
response = requests.get(url)
return url, response.status_code
urls = ["https://example.com", "https://python.org", "https://proglib.io"]
with ThreadPoolExecutor(max_workers=3) as executor:
results = executor.map(fetch_url, urls)
for url, status in results:
print(f"{url}: {status}")
Вывод:
https://example.com: 200
https://python.org: 200
https://proglib.io: 200
Работа с файловой системой
Модуль pathlib
позволяет работать с путями как с объектами, предоставляя интуитивный синтаксис и методы, которые делают код лаконичным и понятным. Преимущества pathlib
по сравнению с os.path
:
- Более простые методы и операции.
- Код работает одинаково на всех операционных системах.
- Есть функционал для чтения, записи, проверки путей, обхода директорий и работы с файлами.
- Объектно-ориентированный подход делает код более структурированным.
Пример: поиск .docx-файлов во всех поддиректориях:
from pathlib import Path
path = Path("C:/Users/Admin/Documents")
for file in path.rglob("*.docx"):
print(file)
При использовании os.path
код получается более громоздким:
import os
path = "C:/Users/Admin/Documents"
for dirpath, dirnames, filenames in os.walk(path):
for filename in filenames:
if filename.endswith(".docx"):
print(os.path.join(dirpath, filename))
Мокинг в модульных тестах
Использование мокинга в модульных тестах – мощный способ изолировать тестируемый код от внешних зависимостей (базы данных, веб-сервисы или сторонние API). Это улучшает стабильность, скорость и повторяемость тестов, а также дает возможность тестировать части системы, которые еще не интегрированы с внешними сервисами.
Пример: мокирование API для получения курса биткоина
import requests
from unittest.mock import patch
def get_bitcoin_price():
response = requests.get("https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd")
data = response.json()
if "bitcoin" not in data:
raise ValueError("Ошибка получения данных от API")
return data["bitcoin"]["usd"]
@patch('requests.get')
def test_get_bitcoin_price(mock_get):
mock_get.return_value.json.return_value = {
"bitcoin": {
"usd": 99000
}
}
price = get_bitcoin_price()
print(f"Текущая стоимость биткоина ${price}.")
test_get_bitcoin_price()
Вывод:
Текущая стоимость биткоина $99000.
Поделитесь своими любимыми Python-приёмами, которые не вошли в статью – какие техники вы считаете обязательными для профессионального разработчика?
Python используется везде: от анализа данных до разработки веб-приложений. Если ты задумываешься о входе в IT или хочешь расширить свой технический стек, обрати внимание на новый курс по основам Python.
Ключевые особенности программы:
- 32 практических урока с фокусом на реальные проекты
- Два опытных преподавателя из индустрии: экс-специалист Мегафона и олимпиадный тренер
- Работа с современными инструментами: PyCharm, Jupyter Notebook
- Создание собственного портфолио из 4 проектов
Программа включает всё необходимое для старта:
- От базового синтаксиса до ООП
- Работа с API и создание ботов
- Парсинг данных и работа с файлами
- Основы алгоритмов и структур данных
Формат обучения адаптивный — учись в своем темпе с постоянной обратной связью от преподавателей.
Комментарии