Python Decorator Example

I am a little versed in decorators from the excellent tutorial on thecodeship , but on one example I was a little embarrassed.

First, the simplest example, followed by an explanation, for the decorator.

def p_decorate(func): def func_wrapper(name): return "<p>{0}</p>".format(func(name)) return func_wrapper def get_text(name): return "lorem ipsum, {0} dolor sit amet".format(name) my_get_text = p_decorate(get_text) print my_get_text("John") 

Now that makes sense to me. A decorator is just a wrapper for a function. And in explaining these guys, he says that a decorator is a function that takes another function as an argument, generates a new function, and returns a generated function that will be used anywhere.

And now the equivalent of the above code:

 def p_decorate(func): def func_wrapper(name): return "<p>{0}</p>".format(func(name)) return func_wrapper @p_decorate def get_text(name): return "lorem ipsum, {0} dolor sit amet".format(name) print get_text("John") 

I believe that I understand how the decorator is initialized when no arguments are given. Correct me if I am wrong.

  • The decorator defaults to the get_text function, and since p_decorate returns the func_wrapper function, we get the true statement get_text = func_wrapper .

What is important to me is the first equivalent of the code, because I see and understand how the decorator works.

What confuses me a lot is the following code:

 def tags(tag_name): def tags_decorator(func): def func_wrapper(name): return "<{0}>{1}</{0}>".format(tag_name, func(name)) return func_wrapper return tags_decorator @tags("p") def get_text(name): return "Hello "+name print get_text("John") 

Again, correct me if I am wrong, but that is my understanding.

  • The decorator accepts the tag string "p" instead of the default function name. And, in turn, the tags_decorator function assumes that the parameter to be passed is a function that get_text executed, get_text .

It may be useful for me to see the equivalent block of code in the form of "non-decorator", but I seem to be unable to wrap around what it starts to look like. I also don't understand why tags_decorator and func_wrapper . What is the purpose of returning two different functions if the decorator only needs to return 1 function to wrap get_text .

As a side note, it really comes down to the following.

  • Is it possible to simplify this block for something less than a set of three functions?
  • Can decorators take more than 1 argument to simplify code?
+5
source share
1 answer

Within the limits, everything after @ is done to create the decorator. In your first example, what follows after @ is just a name:

 @p_decorate 

therefore, Python looks at p_decorate and calls it with a decorated function as an argument:

 get_text = p_decorate(get_text) 

(simplified bit, get_text was not initially bound to the original function, but you already got it).

In your second example, the decorator expression is a bit more active, it includes a call:

 @tags("p") 

therefore, Python uses tags("p") to search for the decorator. In the end, this is what is then done:

 get_text = tags("p")(get_text) 

The output tags("p") here is a decorator! I call the tags function the factory decorator itself, it produces a decorator when called. When you call tags() , it returns tags_decorator() . This is a real decorator here.

Instead, you can remove the decorator function and hardcode "p" code and use it directly:

 def tags_decorator_p(func): def func_wrapper(name): return "<{0}>{1}</{0}>".format("p", func(name)) return func_wrapper @tags_decorator_p def get_text(name): # ... 

but then you will need to create separate decorators for each possible value of the tags() argument. What is the value of the factory decorator, you can add parameters to the decorator and change the way you decorate your function.

The factory decorator can accept any number of arguments; it's just a function that you call to create a decorator. The decorator itself can take only one argument, a function to decorate.

I said within limits at the beginning of my answer for some reason; the syntax of the expression following @ allows only a dotted name ( foo.bar.baz , therefore access to the attribute) and a call ( (arg1, arg2, keyword=arg3) ). See the Help documentation. The original PEP 318 states:

The decorator's statement is limited by what it can accept - arbitrary expressions will not work. Guido preferred this because of the gut feeling [17].

+7
source

Source: https://habr.com/ru/post/1246468/


All Articles