СИЛА ДЕКОРАТОРОВ PYTHON
Эти встроенные декораторы Python могут изменить вашу жизнь
Используйте силу декораторов, чтобы достичь большего с меньшими затратами.
«Простое лучше, чем сложное».
Лучшая функция Python, которая применяет эту философию из «дзен Python», — это декоратор.
Декораторы могут помочь вам написать меньше и более простой код для реализации сложной логики и повторного использования его везде.
Что еще более важно, существует множество замечательных встроенных декораторов Python, которые значительно облегчают нашу жизнь, поскольку мы можем использовать всего одну строку кода, чтобы добавить сложные функции к существующим функциям или классам.
Разговор дешевый. Давайте посмотрим, как я выбрал 69 (я имею в виду 9) декораторов, которые покажут вам, насколько элегантен Python.
1. @lru_cache: ускорьте свои программы за счет кэширования
Самый простой способ ускорить ваши функции Python с помощью трюков с кэшированием — использовать метод @lru_cache
decorator.
Этот декоратор можно использовать для кэширования результатов функции, чтобы последующие вызовы функции с теми же аргументами не выполнялись снова.
Это особенно полезно для функций, требующих значительных вычислительных ресурсов или часто вызываемых с одними и теми же аргументами.
Давайте посмотрим на наглядный пример:
import time def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) start_time = time.perf_counter() print(fibonacci(30)) end_time = time.perf_counter() print(f"The execution time: {end_time - start_time:.8f} seconds") # The execution time: 0.18129450 seconds
Приведенная выше программа вычисляет N-е число Фибоначчи с помощью функции Python. Это отнимает много времени, потому что когда вы вычисляете fibonacci(30)
, многие предыдущие числа Фибоначчи будут вычисляться много раз в процессе рекурсии.
Теперь давайте ускорим его с помощью декоратора @lru_cache
:
from functools import lru_cache import time @lru_cache(maxsize=None) def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) start_time = time.perf_counter() print(fibonacci(30)) end_time = time.perf_counter() print(f"The execution time: {end_time - start_time:.8f} seconds") # The execution time: 0.00002990 seconds
Как видно из приведенного выше кода, после использования декоратора @lru_cache
мы можем получить тот же результат за 0.00002990
секунд, что намного быстрее, чем предыдущие 0.18129450
секунд.
У декоратора @lru_cache
есть параметр maxsize
, который указывает максимальное количество результатов, сохраняемых в кэше. Когда кеш заполнен и необходимо сохранить новый результат, последний использовавшийся результат удаляется из кеша, чтобы освободить место для нового. Это называется стратегией наименее недавно использовавшейся (LRU).
По умолчанию для maxsize
установлено значение 128
. Если для него установлено значение None
, как в нашем примере, функции LRU отключены, и кеш может неограниченно расти.
2. @total_ordering: декоратор класса, который заполняет отсутствующие методы упорядочения
@total_ordering
decorator из модуля functools
используется для создания отсутствующих методов сравнения для класса Python на основе тех, которые определены.
Вот пример:
from functools import total_ordering @total_ordering class Student: def __init__(self, name, grade): self.name = name self.grade = grade def __eq__(self, other): return self.grade == other.grade def __lt__(self, other): return self.grade < other.grade student1 = Student("Alice", 85) student2 = Student("Bob", 75) student3 = Student("Charlie", 85) print(student1 < student2) # False print(student1 > student2) # True print(student1 == student3) # True print(student1 <= student3) # True print(student3 >= student2) # True
Как показано в приведенном выше коде, в классе Student
нет определений для методов __ge__
, __gt__
и __le__
. Однако благодаря декоратору @total_ordering
все результаты наших сравнений между разными экземплярами верны.
Преимущества этого декоратора очевидны:
- Это может сделать ваш код чище и сэкономить ваше время. Так как не нужно писать все методы сравнения.
- Некоторые старые классы могут не определять достаточно методов сравнения. Безопаснее добавить к нему декоратор
@total_ordering
для дальнейшего использования.
Новичок в трейдинге? Попробуйте криптотрейдинговые боты или копи-трейдинг на лучших криптобиржах
3. @contextmanager: создайте настраиваемый контекстный менеджер
В Python есть механизм диспетчера контекста, помогающий правильно управлять ресурсами.
В основном нам просто нужно использовать операторы with
:
with open("test.txt",'w') as f: f.write("Sajawal is writing!")
Как видно из приведенного выше кода, мы можем открыть файл с помощью оператора with
, чтобы он автоматически закрывался после записи. Нам не нужно явно вызывать функцию f.close()
, чтобы закрыть файл.
Иногда нам нужно определить настраиваемый менеджер контекста для некоторых особых требований. В этом случае декоратор @contextmanager — наш друг.
Например, следующий код реализует простой настраиваемый менеджер контекста, который может печатать соответствующую информацию при открытии или закрытии файла.
from contextlib import contextmanager @contextmanager def file_manager(filename, mode): print("The file is opening...") file = open(filename,mode) yield file print("The file is closing...") file.close() with file_manager('test.txt', 'w') as f: f.write('Sajawal is writing!') # The file is opening... # The file is closing...
4. @property: настройка геттеров и сеттеров для классов Python
Геттеры и сеттеры являются важными понятиями объектно-ориентированного программирования (ООП).
Для каждой переменной экземпляра класса метод получения возвращает ее значение, а метод установки устанавливает или обновляет ее значение. Учитывая это, геттеры и сеттеры также известны как аксессоры и мутаторы соответственно.
Они используются для защиты ваших данных от прямого и неожиданного доступа или изменения.
Различные языки ООП имеют разные механизмы определения геттеров и сеттеров. В Python мы можем просто использовать декоратор @property
.
class Student: def __init__(self): self._score = 0 @property def score(self): return self._score @score.setter def score(self, s): if 0 <= s <= 100: self._score = s else: raise ValueError('The score must be between 0 ~ 100!') Sajawal = Student() Sajawal.score=99 print(Sajawal.score)# 99 Sajawal.score = 999 # ValueError: The score must be between 0 ~ 100!
Как видно из приведенного выше примера, переменная score
не может быть установлена равной 999, что является бессмысленным числом. Потому что мы ограничили его допустимый диапазон внутри функции установки с помощью декоратора @property
.
Без сомнения, добавление этого сеттера может успешно избежать неожиданных ошибок или результатов.
5. @cached_property: кэшируйте результат метода как атрибут
Python 3.8 представил новый мощный декоратор модуля functool
— @cached_property
. Он может преобразовать метод класса в свойство, значение которого вычисляется один раз, а затем кэшируется как обычный атрибут на время жизни экземпляра.
Вот пример:
from functools import cached_property class Circle: def __init__(self, radius): self.radius = radius @cached_property def area(self): return 3.14 * self.radius ** 2 circle = Circle(10) print(circle.area) # prints 314.0 print(circle.area) # returns the cached result (314.0) directly
В приведенном выше коде мы декорировали метод area
с помощью метода @cached_property
. Таким образом, нет повторяющихся вычислений для circle.area
одного и того же неизменного экземпляра.
6. @classmethod: определение методов класса в классе Python
Внутри класса Python есть 3 возможных типа методов:
- Методы экземпляра: методы, привязанные к экземпляру. Они могут получать доступ к данным экземпляра и изменять их. Метод экземпляра вызывается для экземпляра класса и может получить доступ к данным экземпляра через параметр
self
. - Методы класса: методы, привязанные к классу. Они не могут изменять данные экземпляра. Метод класса вызывается для самого класса и получает класс в качестве первого параметра, который условно называется
cls
. - Статические методы: методы, не привязанные к экземпляру или классу.
Методы экземпляра могут быть определены как обычные функции Python, если их первый параметр равен self
. Однако для определения метода класса нам нужно использовать декоратор @classmethod
.
Для демонстрации в следующем примере определяется метод класса, который можно использовать для получения экземпляра Circle
через диаметр:
class Circle: def __init__(self, radius): self.radius = radius @classmethod def from_diameter(cls, diameter): return cls(diameter / 2) @property def diameter(self): return self.radius * 2 @diameter.setter def diameter(self, diameter): self.radius = diameter / 2 c = Circle.from_diameter(8) print(c.radius) # 4.0 print(c.diameter) # 8.0
7. @staticmethod: определение статических методов в классе Python
Как уже упоминалось, статические методы не привязаны к экземпляру или классу. Они включены в класс просто потому, что логически принадлежат ему.
Статические методы обычно используются в служебных классах, которые выполняют группу связанных задач, таких как математические вычисления. Организуя связанные функции в статические методы внутри класса, наш код станет более организованным и понятным.
Чтобы определить статический метод, нам просто нужно использовать декоратор @staticmethod. Давайте посмотрим на пример:
class Student: def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name self.nickname = None def set_nickname(self, name): self.nickname = name @staticmethod def suitable_age(age): return 6 <= age <= 70 print(Student.suitable_age(99)) # False print(Student.suitable_age(27)) # True print(Student('me', 'you').suitable_age(27)) # True
8. @dataclass: определяйте специальные классы с меньшим количеством кода
Декоратор @dataclass
(представленный в Python 3.7) может автоматически генерировать несколько специальных методов для класса, таких как __init__
, __repr__
, __eq__
, __lt__
и так далее.
Следовательно, это может сэкономить нам много времени при написании этих основных методов. Если класс в основном используется для хранения данных, декоратор @dataclass
— наш лучший друг.
Чтобы продемонстрировать, в следующем примере просто определяются два поля данных класса с именем Point
. Благодаря декоратору @dataclass
его достаточно использовать:
from dataclasses import dataclass @dataclass class Point: x: float y: float point = Point(1.0, 2.0) print(point) # Point(x=1.0, y=2.0)
9. @atexit.register: регистрация функции, которая будет выполняться после нормального завершения программы.
Декоратор @register
из модуля atexit
может позволить нам выполнить функцию при выходе из интерпретатора Python.
Этот декоратор очень полезен для выполнения последних задач, таких как освобождение ресурсов или просто прощание!
Вот пример:
import atexit @atexit.register def goodbye(): print("Bye bye!") print("Hello Reader!")
Выходы:
Hello Reader! Bye bye!
Как видно из примера, из-за использования декоратора @register
терминал напечатал «Пока-пока!» даже если мы не вызывали функцию goodbye
явно.
Присоединяйтесь к Coinmonks, Каналу Telegram и Каналу Youtube, узнайте о криптотрейдинге и инвестировании