Питхон Децораторс: Како га користити и зашто?

Декоратор узима функцију, додаје неку функционалност и враћа је. У овом упутству ћете научити како можете створити декоратор и зашто бисте га требали користити.

Декоратори у Питхону

Питхон има занимљиву функцију која се назива декоратори за додавање функционалности постојећем коду.

То се такође назива метапрограмирањем, јер део програма покушава да модификује други део програма током компајлирања.

Предуслови за учење декоратера

Да бисмо разумели декоратере, прво морамо знати неколико основних ствари у Питхону.

Морамо бити задовољни чињеницом да су све ствари у Питхону (да! Чак и класе) објекти. Имена која дефинишемо су једноставно идентификатори везани за ове објекте. Функције нису изузетак, оне су такође објекти (са атрибутима). Разна различита имена могу бити везана за исти објект функције.

Ево примера.

 def first(msg): print(msg) first("Hello") second = first second("Hello")

Оутпут

 здраво здраво

Када покренете код, обе функције firstи secondдају исти излаз. Овде се имена firstи secondодносе на исти објект функције.

Сад ствари постају чудније.

Функције се могу проследити као аргументи другој функцији.

Ако сте користили функције као што су map, filterи reduceна Питхон, онда већ знате о томе.

Такве функције које узимају друге функције као аргументе називају се и функције вишег реда . Ево примера такве функције.

 def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result

Функцију позивамо на следећи начин.

 >>> operate(inc,3) 4 >>> operate(dec,3) 2

Даље, функција може вратити другу функцију.

 def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() # Outputs "Hello" new()

Оутпут

 Здраво

Овде is_returned()је угнежђена функција која се дефинише и враћа сваки пут када позовемо is_called().

Коначно, морамо знати о затварањима у Питхону.

Повратак на Декораторе

Функције и методе називају се позивима како се и могу назвати.

У ствари, сваки објекат који примењује посебну __call__()методу назива се позивом. Дакле, у најосновнијем смислу, декоратор је позивни који враћа позивни.

У основи, декоратор узима функцију, додаје неку функционалност и враћа је.

 def make_pretty(func): def inner(): print("I got decorated") func() return inner def ordinary(): print("I am ordinary")

Када покренете следеће кодове у љусци,

 >>> ordinary() I am ordinary >>> # let's decorate this ordinary function >>> pretty = make_pretty(ordinary) >>> pretty() I got decorated I am ordinary

У примеру приказаном горе make_pretty()је декоратор. У кораку додељивања:

 pretty = make_pretty(ordinary)

Функција ordinary()је украшена и враћена функција је добила име pretty.

Видимо да је функција декоратора додала нову функцију оригиналној функцији. Ово је слично паковању поклона. Декоратор делује као омотач. Природа предмета који је украшен (стварни поклон изнутра) се не мења. Али сада изгледа прилично (откад је украшен).

Генерално, украшавамо функцију и додељујемо је као,

 ordinary = make_pretty(ordinary).

Ово је уобичајена конструкција и из тог разлога Питхон има синтаксу да то поједностави.

@Симбол можемо користити заједно са називом функције декоратера и поставити га изнад дефиниције функције која се украшава. На пример,

 @make_pretty def ordinary(): print("I am ordinary")

је еквивалентно са

 def ordinary(): print("I am ordinary") ordinary = make_pretty(ordinary)

Ово је само синтаксички шећер за примену декоратора.

Декорација функција параметрима

Горњи декоратор је био једноставан и радио је само са функцијама које нису имале никакве параметре. Шта би било да имамо функције које узимају параметре као што су:

 def divide(a, b): return a/b

Ова функција има два параметра, а и б. Знамо да ће дати грешку ако у б пренесемо 0.

 >>> divide(2,5) 0.4 >>> divide(2,0) Traceback (most recent call last):… ZeroDivisionError: division by zero

Хајде сада да направимо декоратор који ће проверити овај случај који ће проузроковати грешку.

 def smart_divide(func): def inner(a, b): print("I am going to divide", a, "and", b) if b == 0: print("Whoops! cannot divide") return return func(a, b) return inner @smart_divide def divide(a, b): print(a/b)

Ова нова имплементација ће се вратити Noneако се појави услов грешке.

 >>> divide(2,5) I am going to divide 2 and 5 0.4 >>> divide(2,0) I am going to divide 2 and 0 Whoops! cannot divide

На овај начин можемо украсити функције које узимају параметре.

Оштри посматрач приметиће да су параметри угнежђене inner()функције унутар декоратера исти као и параметри функција које он украшава. Узимајући ово у обзир, сада можемо да направимо опште декоратере који раде са било којим бројем параметара.

In Python, this magic is done as function(*args, **kwargs). In this way, args will be the tuple of positional arguments and kwargs will be the dictionary of keyword arguments. An example of such a decorator will be:

 def works_for_all(func): def inner(*args, **kwargs): print("I can decorate any function") return func(*args, **kwargs) return inner

Chaining Decorators in Python

Multiple decorators can be chained in Python.

This is to say, a function can be decorated multiple times with different (or same) decorators. We simply place the decorators above the desired function.

 def star(func): def inner(*args, **kwargs): print("*" * 30) func(*args, **kwargs) print("*" * 30) return inner def percent(func): def inner(*args, **kwargs): print("%" * 30) func(*args, **kwargs) print("%" * 30) return inner @star @percent def printer(msg): print(msg) printer("Hello")

Output

 ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************

The above syntax of,

 @star @percent def printer(msg): print(msg)

is equivalent to

 def printer(msg): print(msg) printer = star(percent(printer))

The order in which we chain decorators matter. If we had reversed the order as,

 @percent @star def printer(msg): print(msg)

The output would be:

 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** Hello ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Занимљиви Чланци...