Ответ
В Python декораторы — это функции, которые изменяют или расширяют поведение других функций или методов без изменения их исходного кода. Вот основные встроенные декораторы и те, что часто используются из стандартной библиотеки functools:
-
@property- Назначение: Позволяет превратить метод класса в атрибут, обеспечивая контролируемый доступ к данным (геттер).
- Почему: Используется для инкапсуляции, создания "вычисляемых" атрибутов и обеспечения валидации при установке значений (через
@<attr>.setter). Улучшает читаемость кода, позволяя обращаться к методам как к атрибутам. -
Пример:
class Circle: def __init__(self, radius): self._radius = radius # Приватный атрибут @property def radius(self): """Геттер для радиуса.""" return self._radius @radius.setter def radius(self, value): """Сеттер для радиуса с валидацией.""" if value < 0: raise ValueError("Радиус не может быть отрицательным") self._radius = value c = Circle(10) print(c.radius) # Вызывает геттер: 10 c.radius = 15 # Вызывает сеттер print(c.radius) # 15 # c.radius = -5 # Вызовет ValueError
-
@classmethod- Назначение: Объявляет метод как метод класса. Первый аргумент такого метода — сам класс (
cls), а не экземпляр (self). - Почему: Полезен для создания альтернативных конструкторов (фабричных методов), которые могут создавать экземпляры класса различными способами, или для операций, которые относятся ко всему классу, а не к конкретному экземпляру.
-
Пример:
class MyClass: def __init__(self, data): self.data = data @classmethod def from_string(cls, string_data): """Фабричный метод для создания экземпляра из строки.""" return cls(int(string_data)) @classmethod def default_instance(cls): """Создает экземпляр с данными по умолчанию.""" return cls(0) obj1 = MyClass(10) obj2 = MyClass.from_string("20") obj3 = MyClass.default_instance() print(obj1.data, obj2.data, obj3.data) # Вывод: 10 20 0
- Назначение: Объявляет метод как метод класса. Первый аргумент такого метода — сам класс (
-
@staticmethod- Назначение: Объявляет метод как статический. Он не принимает ни
self, ниclsв качестве первого аргумента и ведет себя как обычная функция, логически связанная с классом. - Почему: Используется для утилитарных функций, которые не зависят от состояния экземпляра или класса, но логически принадлежат классу. Помогает организовать код.
-
Пример:
class MathUtils: @staticmethod def add(a, b): """Статический метод для сложения двух чисел.""" return a + b @staticmethod def multiply(a, b): """Статический метод для умножения двух чисел.""" return a * b print(MathUtils.add(5, 3)) # Вывод: 8 print(MathUtils.multiply(5, 3)) # Вывод: 15
- Назначение: Объявляет метод как статический. Он не принимает ни
-
@functools.cache(Python 3.9+)- Назначение: Декоратор для мемоизации (кеширования) результатов функции. Сохраняет результаты вызовов функции с одними и теми же аргументами.
- Почему: Значительно повышает производительность для чистых функций (без побочных эффектов), которые часто вызываются с одними и теми же аргументами и выполняют дорогостоящие вычисления.
-
Пример:
import functools import time @functools.cache def fibonacci(n): if n <= 1: return n return fibonacci(n - 1) + fibonacci(n - 2) start_time = time.time() fibonacci(30) # Вычисляется один раз print(f"Time for first call: {time.time() - start_time:.4f}s") start_time = time.time() fibonacci(30) # Результат берется из кеша print(f"Time for second call: {time.time() - start_time:.4f}s")
-
@functools.lru_cache- Назначение: Декоратор для кеширования результатов функции с ограничением по размеру (Least Recently Used - наименее недавно использованные).
- Почему: Позволяет управлять потреблением памяти кешем, автоматически удаляя наименее недавно использованные элементы, когда кеш достигает максимального размера. Идеален для функций, работающих с ограниченным набором часто используемых входных данных.
-
Пример:
import functools @functools.lru_cache(maxsize=128) # Кеш на 128 последних результатов def get_web_page(url): """Имитация дорогостоящего запроса к веб-странице.""" print(f"Fetching {url}...") # time.sleep(1) # Имитация задержки return f"Content of {url}" print(get_web_page("http://example.com/page1")) # Вывод: Fetching http://example.com/page1... print(get_web_page("http://example.com/page2")) # Вывод: Fetching http://example.com/page2... print(get_web_page("http://example.com/page1")) # Результат берется из кеша, без 'Fetching...'
Ответ 18+ 🔞
А, ну вот, опять эти декораторы, блядь! Слушай, давай разберём, как нормальные люди, а не как эти заумные учебники, которые мозги выносят. Представь, что декоратор — это как наклейка на функцию, которая её улучшает, не лезя внутрь. Красота, ёпта!
1. @property — чтоб не пиздеть лишнего
Ну, типа, сделал ты в классе приватный атрибут _radius, а потом думаешь: "Блин, а как к нему красиво обращаться, да ещё и проверку впихнуть?". Вот @property и спасает. Он превращает метод в атрибут, чтоб ты мог писать circle.radius, а не circle.get_radius(). А если хочешь менять значение — прикручиваешь сеттер. Без него — только читать, сука.
class Circle:
def __init__(self, radius):
self._radius = radius # Спрятали, чтоб не ебали
@property
def radius(self):
"""Геттер для радиуса."""
return self._radius
@radius.setter
def radius(self, value):
"""Сеттер для радиуса с валидацией."""
if value < 0:
raise ValueError("Радиус не может быть отрицательным, ты чё, охренел?")
self._radius = value
c = Circle(10)
print(c.radius) # 10 — просто как атрибут, а на деле метод!
c.radius = 15 # Всё ок
# c.radius = -5 # Вылетит с криком "ValueError", пиздец
2. @classmethod — фабрика по производству объектов
Это когда тебе надо создать объект не через __init__, а как-то по-хитрому. Например, из строки, из JSON'а, или вообще готовый шаблонный экземпляр. Первый аргумент — cls (сам класс), а не self. По сути, альтернативный конструктор, ёбта!
class MyClass:
def __init__(self, data):
self.data = data
@classmethod
def from_string(cls, string_data):
"""Выковыриваем число из строки и делаем объект."""
return cls(int(string_data))
@classmethod
def default_instance(cls):
"""Дефолтный объект, чтоб не париться."""
return cls(0)
obj1 = MyClass(10)
obj2 = MyClass.from_string("20") # Вот она, магия!
obj3 = MyClass.default_instance()
print(obj1.data, obj2.data, obj3.data) # 10 20 0
3. @staticmethod — обычная функция, которая притворяется частью класса
Ни self, ни cls не получает. Просто висит внутри класса для порядка, чтоб не захламлять пространство имён. Типа утилитка, которая по смыслу относится к классу, но не лезет в его внутренности.
class MathUtils:
@staticmethod
def add(a, b):
"""Сложить два числа — что может быть проще?"""
return a + b
@staticmethod
def multiply(a, b):
"""Умножить, блядь."""
return a * b
print(MathUtils.add(5, 3)) # 8
print(MathUtils.multiply(5, 3)) # 15
4. @functools.cache (Python 3.9+) — память как у слона
Охуенная штука для тяжёлых функций, которые постоянно вызываются с одними и теми же аргументами. Запоминает результат и в следующий раз не вычисляет, а сразу отдаёт из кеша. Но будь осторожен — кеш растёт бесконечно, если функция вызывается с разными аргументами.
import functools
import time
@functools.cache
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
start_time = time.time()
fibonacci(30) # Считает долго, пиздец
print(f"Первый раз: {time.time() - start_time:.4f}s")
start_time = time.time()
fibonacci(30) # А тут уже мгновенно, из кеша!
print(f"Второй раз: {time.time() - start_time:.4f}s")
5. @functools.lru_cache — умный кеш с ограничением
То же самое, но с лимитом по размеру. Если кеш переполнится — выкинет самые старые (давно не использованные) результаты. Идеально, когда аргументов много, но часто повторяются только некоторые.
import functools
@functools.lru_cache(maxsize=128) # Храним 128 последних результатов
def get_web_page(url):
"""Представь, что тут долгий запрос в интернет."""
print(f"Качаю {url}...")
# time.sleep(1) # Имитация задержки
return f"Контент {url}"
print(get_web_page("http://example.com/page1")) # Напечатает "Качаю..."
print(get_web_page("http://example.com/page2")) # Опять "Качаю..."
print(get_web_page("http://example.com/page1")) # А тут уже тишина, взял из кеша!
Вот и вся магия, блядь. Главное — не переусердствуй, а то задекорируешь всё подряд и потом сам не разберёшься, что к чему. Удачи, ёпта!