“`html
Декоратори Python — це потужна функція, яка дозволяє змінювати поведінку функції чи класу, не торкаючись їхнього вихідного коду. По суті, це функції, які приймають іншу функцію як аргумент і повертають нову функцію, що обгортає оригінальну. Це дає можливість додавати додаткову функціональність або логіку до вихідної функції без її редагування.
Наприклад, уявіть функцію, яка виводить повідомлення на консоль:
def hello():
print("Hello, world!")
Тепер, якщо ви хочете виміряти час виконання цієї функції, можна написати окремий механізм, що використовує модуль time для обчислення тривалості, а потім викликати оригінальну функцію:
import time
def measure_time(func):
def wrapper():
start = time.time()
func()
end = time.time()
print(f"Execution time: {end - start} seconds")
return wrapper
Функція measure_time повертає іншу функцію — wrapper, яка є модифікованою версією оригінальної. wrapper фіксує час початку й завершення виконання та викликає вихідну функцію.
Щоб скористатися цим, можна зробити так:
hello = measure_time(hello)
hello()
Результат буде виглядати приблизно так:
Hello, world!
Execution time: 0.000123456789 seconds
Таким чином, ми успішно додали нову функціональність до hello, не змінюючи її початковий код. Проте, декоратори пропонують ще елегантніший і лаконічніший спосіб реалізації цього.
Декоратори — це, по суті, синтаксичний цукор, який дозволяє застосовувати одну функцію до іншої за допомогою символу @.
Наприклад, попередній код можна переписати так:
@measure_time
def hello():
print("Hello, world!")
hello()
Це дасть той самий результат, але з меншою кількістю коду. Рядок @measure_time еквівалентний запису hello = measure_time(hello), але виглядає набагато чистіше та зрозуміліше.
Навіщо використовувати декоратори Python?
Декоратори Python корисні з багатьох причин:
- Вони дозволяють повторно використовувати код та уникати дублювання. Наприклад, якщо багато функцій потребують вимірювання часу виконання, можна застосувати один і той самий декоратор замість багаторазового написання ідентичного коду.
- Вони допомагають розділяти завдання та дотримуватися принципу єдиної відповідальності. Якщо функція виконує складні обчислення, декоратор може забезпечити логування, обробку помилок, кешування або валідацію вхідних/вихідних даних, не заважаючи основній логіці функції.
- Вони дають змогу розширювати функціональність існуючих функцій або класів без зміни їхнього вихідного коду. Наприклад, при роботі зі сторонніми бібліотеками, декоратори можуть додати бажані можливості чи поведінку, обгорнувши оригінальні функції чи класи.
Деякі приклади декораторів Python
У Python існує багато вбудованих декораторів, таких як @staticmethod, @classmethod, @property, @functools.lru_cache, @functools.singledispatch та інші. Також можна створювати власні декоратори для різних завдань.
Ось кілька прикладів, які можуть значно спростити ваш код:
Декоратор @timer
Цей декоратор схожий на @measure_time, але він універсальніший: підходить для будь-якої функції, незалежно від її аргументів чи значення, що повертається. Він використовує @functools.wraps для збереження оригінального імені та документації функції.
Ось код:
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"Execution time of {func.__name__}: {end - start} seconds")
return result
return wrapper
Тепер його можна застосовувати для вимірювання часу виконання будь-якої функції:
@timer
def factorial(n):
"""Returns the factorial of n"""
if n == 0 or n == 1:
return 1
else:
return n * factorial(n - 1)
@timer
def fibonacci(n):
"""Returns the nth Fibonacci number"""
if n == 0 or n == 1:
return n
else:
return fibonacci(n - 1) + fibonacci(n - 2)
print(factorial(10))
print(fibonacci(10))
Результат буде приблизно таким:
Execution time of factorial: 1.1920928955078125e-06 seconds
3628800
Execution time of fibonacci: 0.000123456789 seconds
55
Декоратор @debug
Цей декоратор корисний для налагодження, оскільки він виводить ім’я функції, її аргументи та результат, що повертається. Він також використовує @functools.wraps.
Ось код:
from functools import wraps
def debug(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args: {args} and kwargs: {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned: {result}")
return result
return wrapper
Його можна застосовувати для налагодження різних функцій:
@debug
def add(x, y):
"""Returns the sum of x and y"""
return x + y
@debug
def greet(name, message="Hello"):
"""Returns a greeting message with the name"""
return f"{message}, {name}!"
print(add(2, 3))
print(greet("Alice"))
print(greet("Bob", message="Hi"))
Вивід буде таким:
Calling add with args: (2, 3) and kwargs: {}
add returned: 5
5
Calling greet with args: ('Alice',) and kwargs: {}
greet returned: Hello, Alice!
Hello, Alice!
Calling greet with args: ('Bob',) and kwargs: {'message': 'Hi'}
greet returned: Hi, Bob!
Hi, Bob!
Декоратор @memoize
Цей декоратор призначений для оптимізації продуктивності рекурсивних або обчислювально дорогих функцій. Він кешує результати попередніх викликів і повертає їх, якщо функція викликається з тими ж аргументами. Також використовується @functools.wraps.
Код:
from functools import wraps
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args in cache:
return cache[args]
else:
result = func(*args)
cache[args] = result
return result
return wrapper
Застосування до функцій:
@memoize
def factorial(n):
"""Returns the factorial of n"""
if n == 0 or n == 1:
return 1
else:
return n * factorial(n - 1)
@memoize
def fibonacci(n):
"""Returns the nth Fibonacci number"""
if n == 0 or n == 1:
return n
else:
return fibonacci(n - 1) + fibonacci(n - 2)
print(factorial(10))
print(fibonacci(10))
Результат буде ідентичним попереднім прикладам, але час виконання буде значно меншим завдяки кешуванню.
Висновок
Декоратори Python — це потужний та елегантний спосіб модифікувати поведінку функцій або класів, не змінюючи їхнього вихідного коду. Вони допомагають скоротити код, покращити його читабельність, забезпечити повторне використання, відокремити функціональність та розширити можливості існуючого коду.
“`
За даними порталу: highload.tech
