Навіщо декоратори у Python

Що таке Декоратор в Python?

У Python декоратори є потужним механізмом для зміни поведінки функцій або методів, не змінюючи їх вихідний код. Декоратори часто використовуються для додавання функціональності до існуючих функцій, логування, перевірок прав доступу або перетворення результатів.

Приклад використання:

def uppercase_decorator(func): def wrapper(*args, **kwargs): result = func(*args, **kwargs) return result.upper() return wrapper @uppercase_decorator def greet(name): return "Hello, " + name # Виклик функції після застосування декоратора greeting = greet("Alice") print("Привітання:", greeting) # Висновок: Привітання: HELLO, ALICE

У цьому прикладі створюється декоратор uppercase_decorator , який перетворює результат функції, що викликається у верхній регістр. Потім цей декоратор застосовується до функції greet . Під час виклику greet(“Alice”) результат перетворюється на верхній регістр і виводиться на екран.

Декоратори дозволяють лаконічно змінювати функціональність коду, роблячи його більш чистим та підтримуваним. У Python існує безліч вбудованих декораторів, а також можливість створювати власні для задоволення конкретних потреб у проекті.

Знайомство з декораторами в Python та способи їх використання

Всім, хто хоч трохи працює з Python, знайомі декоратори. Декоратори — це обгортка для функції. Найкраще показати все на прикладі:

>>> def dec(fn): . def func_wrapper(): . print("Before") . fn() . print("After") . return func_wrapper . >>> @dec . def hello(): . print("Hello World!") . >>> hello() Before Hello World! After 

Тут dec — і є наш декоратор. Він оголошується так само як функція, аргументом приймає функцію для обробки. Всередині декоратора оголошується нова функція (в яку ви можете додати потрібні вам дії) і повертається. Для декорування функції перед її об’явою потрібно написати @decorator_name . Замість декорованої функції буде викликатися та, що створена всередині декоратора. Тепер, після невеликого вступу, давайте подивимось для чого ж їх можна використовувати.

Замірювання часу виконання

Декоратори можна використовувати для невеличких бенчмарків. Наприклад, так:

>>> import time >>> from random import randint >>> >>> def ex_time(fn): . def wrap(*args, **kwargs): . start = time.time() . res = fn(*args, **kwargs) . end = time.time() . print("Time: <>s.".format(round(end-start, 5))) . return res . return wrap . >>> @ex_time . def slow_fn(x): . # імітуємо вичислення . time.sleep(randint(1000, 3000)/1000) . return x*x*x . >>> slow_fn(3) Time: 2.71892s. 27 

А можна цю версію трохи поліпшити. Декоратори також можуть приймати аргументи. Знаючи це, давайте напишемо покращену версію. Для цього сам декоратор потрібно обгорнути іншим декоратором (безумство, так):

>>> def ex_time(count): . def wrapper(fn): . def wrap(*args, **kwargs): . times = [] . for i in range(count): . start = time.time() . res = fn(*args, **kwargs) . end = time.time() . times.append(round(end-start, 5)) . print("Avg. time s. \ Max time s. \ Min time s." . .format(avg=sum(times)/count, max=max(times), min=min(times))) . return res . return wrap . return wrapper . >>> @ex_time(3) . def slow_fn(x): . # імітуємо вичислення . time.sleep(randint(1000, 3000)/1000) . return x*x*x . >>> slow_fn(3) Avg. time 2.1003s. Max time 2.53272s. Min time 1.42262s. 27 

Перевірка типів

Якщо ви сумуєте за Java або іншою статично типізованою мовою, то в мене для вас гарні новини: статичну типізацію можна частково прикрутити й до Python. Так, звісно, в нових версіях є вбудовані нотації типів, але вони містять лише рекомендаційний характер і ні на що не впливають.

>>> def typed(*types, **kwtypes): . def wrapper(fn): . def wrap(*f_args, **f_kwargs): . for i in range(len(types)): . if type(f_args[i]) != types[i]: . raise TypeError() . for k in kwtypes: . if type(f_kwargs[k]) != kwtypes[k]: . raise TypeError() . return fn(*f_args, **f_kwargs) . return wrap . return wrapper . >>> @typed(int, int, int) . def typed_fn(x, y, z): . return x*y*z . >>> @typed(str, str, str) . def typed_fn2(x, y, z): . return x+y+z . >>> @typed(str, str, z=int) . def typed_fn3(x, y, z="Hello! "): . return (x+y)*z . >>> typed_fn(3, 4, 5) 60 >>> # Error: . typed_fn(3, 4, "Doge") Traceback (most recent call last): File "", line 2, in File "", line 6, in wrap TypeError >>> >>> typed_fn2("Hello, ", "Codeguida", "!") 'Hello, Codeguida!' >>> typed_fn3("Hello, ", "Codeguida! ", z=5) 'Hello, Codeguida! Hello, Codeguida! Hello, Codeguida! Hello, Codeguida! Hello, Codeguida! ' 

Транзакції

Транзакція в контексті баз даних — сеанс роботи з БД, в якому всі зміни можна відмінити одним лише викликом. Уявіть, ваш скрипт інтенсивно працює з БД, наповнюючи її, і тут вилітає якийсь Exception. І тепер у вас повна база не валідних записів, адже скрипт не завершив свою роботу. А якби ви використовували транзакції всі ці зміни можна було б відмінити викликавши щось типу db.rollback() . Давайте напишемо декоратор, що буде обгортати наші дії с БД в транзакцію:

def transaction(db): def wrapper(fn): def wrap(*args, **kwargs): res = None db.start_transaction() try: res = fn(*args, **kwargs) except Exception as e: db.rollback() raise e else: db.end_transaction() return res return wrap return wrapper @transaction(db) def action(db): db.ids.insert(1/0) 

Висновок

Ось так можна використовувати ще один ninja-hack в Python — декоратори. Це доволі потужний інструмент, але він не підходить для багатьох задач. Тож треба бути обережним використовуючи його.

python декоратори decorators

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter

Related Post

Як позбутися запаху несвіжої рибиЯк позбутися запаху несвіжої риби

Як усунути неприємний запах свіжої риби з кухні: чотири ефективні способи Дізнайтеся про методи видалення неприємного запаху свіжої риби з кухні. OBOZ.UA розібрався, як правильна вентиляція і натуральні інгредієнти можуть

Скільки в англійській французьких слівСкільки в англійській французьких слів

Знаючи це явище, а саме як слово походить від іншої мови, студенти можуть проводити паралелі між мовами, і тим саме відбувається краще запам'ятовування слів. Роль запозичень (borrowings, loan-words) в різних