L
L
lutokris2021-06-18 15:07:36
Python
lutokris, 2021-06-18 15:07:36

How do you understand decorators in Python?

I've been sitting for 3 days already .. for the life of me, I don't understand at all. I'm sitting writing code, it works, but I don't understand - how? where and what, why? Let's have an example like this:

def uppercase(func):
    def  wrapper():
        original_result = func()
        return f'Большие {original_result.upper()}'
    return wrapper

@uppercase
def greet():
    return 'маленькие буквы'

print(greet())
>>>Большие МАЛЕНЬКИЕ БУКВЫ

If I understand correctly, the uppercase function accepts another function (more precisely, an object). Inside it, another function is described called wrapper, inside which
string operations are performed with the result of executing another function. Then I had a question and a stupor - for some reason we do not return the result of the execution of the wrapper function, but return the function-object itself. And it itself is somehow called and
executed, although we do not explicitly write it. If I understand correctly, when we decorate the uppercase function and write def greet, we're like just creating an uppercase(greet) function that will always be called when we write greet()? And why then if uppercase(greet) returns an object of type function, how is it itself called? In general, with these decorators I have a complete stupor.

Answer the question

In order to leave comments, you need to log in

3 answer(s)
O
o5a, 2021-06-19
@lutokris

Then I had a question and a stupor - for some reason we do not return the result of the execution of the wrapper function, but return the function-object itself.

And there is. We do not need to call the function in the decorator itself, but only indicate which function we are using. Without the brackets (), it's just a reference to the object (function) itself.
Example:
def f1(): return 1

a = f1() # присваиваем результат работы функции, т.е. в a будет 1
a = f1 # присваиваем саму функцию
# тогда a - это не результат работы функции f1, а сама функция f1
# и мы можем ее запустить
print(a() ) # выведет 1, т.к. мы через a запустили f1

Decorators do just that. Create a function and return it, but don't run it. And this "wrapper" is launched during the launch of our main function.
Those. when the decorator is added
@uppercase
def greet(): ...
# то когда мы запускаем функцию
greet()
# по факту запускается
uppercase(greet)()

But what does uppercase(greet) return? Let's look at the code. And it returns some wrapper. What does a wrapper do?
original_result = func() # запускает переданную ему функцию на исполнение, в нашем случае greet
return f'Большие {original_result.upper()}' # и возвращает строку с результатами исполнения

And it itself is somehow called and executed, although we do not explicitly write it.

Executed because running the decorated function means running
uppercase(greet)()
а т.к. uppercase(greet) возвращает wrapper
то uppercase(greet)() запускает wrapper()

D
Drill, 2021-06-18
@Drill

do we just create a function uppercase(greet) that will always be called when we write greet()? And why then if uppercase(greet) returns an object of type function, how is it itself called?

@uppercase
def greet():
    return 'маленькие буквы'

is tantamount to
def greet():
    return 'маленькие буквы'

greet = uppercase(greet)

What is the purpose of decorators?

D
Dmitry Shitskov, 2021-06-18
@Zarom

You can read more about the technical implementation in this article https://habr.com/en/post/141411/
In short, the following happens - the decorator sets the link greet = wrapper, so when greet() is called, wrapper() is called instead, and not defined in the original greet code.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question