Why do we need wrapper function in decorators?

安稳与你 提交于 2020-01-01 02:53:06

问题


If I create a decorator like following:

def my_decorator(some_fun):
    def wrapper():
        print("before some_function() is called.")
        some_fun()
        print("after some_function() is called.")
    return wrapper

@my_decorator
def just_some_function():
    print("Wheee!")

Another decorator can be defined as:

def my_decorator(some_fun):
    print("before some_function() is called.")
    some_fun()
    print("after some_function() is called.")

@my_decorator
def just_some_fun():
    print("some fun")

Both decorators will work the same. What is the benefit of using "wrapper" function inside decorator. I didn't get the purpose.


回答1:


The purpose of having a wrapper function is that a function decorator receives a function object to decorate, and it must return the decorated function.

Your 2nd version of my_decorator doesn't have an explicit return statement, so it returns None. When my_decorator is called via the @ decorator syntax

before some_function() is called.
some fun
after some_function() is called.

gets printed, and then None gets assigned to the name just_some_fun. So if you add print(just_some_fun) to the end of that code it will print None.

It may be easier to understand what's going on if we get rid of the @ syntactic sugar and re-write your code using normal function calling syntax:

def my_decorator(some_fun):
    print("before some_function() is called.")
    some_fun()
    print("after some_function() is called.")

def just_some_fun():
    print("some fun")

just_some_fun = my_decorator(just_some_fun)



回答2:


The decorators in Python are callable objects which in the simplest case is function taking one parameter which is some function or class. The decorator should return again same type which it takes (so if it takes function it should return function). The point is in the time when the decorator is called.

When you import a Python file or just run it directly the Python interpreter goes through the content and gathering information about what classes and function are defined and if it encounter on some code (not declaration) it will execute it.

If the interpreter encounter on decorator it takes the decorated function, call the decorator and replace the decorated function with returned value from the decorator.

Let's say you have this code:

@my_decorator
def my_function()
  print("My_function")

It's equivalent to this call:

def my_function()
    print("My_function")    

my_function = my_decorator(my_function)

If the decorator would be like this one

def my_decorator(func):
    print("decorated)
    return 42

then the my_function is not even a function it would be an integer (you can try print(my_function))

So when you define decorator as

def my_decorator2(some_fun):
    print("before")
    some_fun()
    print("after")

then this decorator returns nothing (in python it means it returns None).

@my_decorator2
def decorated():
  print("inside")

prints

before
inside
after

but calling decorated() will raise exception 'NoneType' object is not callable because the decorated was replaced with None.

You should always create decorator which return something useful like function or class (which is usually the "wrap" function inside). Sometimes can be useful to return from decorator something else then function/class but it usually obfuscate your code and convert it into something totally non-maintainable.




回答3:


Its already explained why to use wrapper function, out of curiosity I am just giving examples what we can do if we don't need a wrapper function.

Type 1

Return a small function which returns None or pass

def decorator_func(to_be_decorated_function):
    print("Logging IN: Currrently  in function")
    to_be_decorated_function()
    print("Logging OUT: Currrently  in function")

    def a(): None  # or def a(): pass

    return (a)

@decorator_func
def to_be_decorated_function():
    print('October 16, 2000')

to_be_decorated_function()
# equivalent to 
#to_be_decorated_function = decorator_func(to_be_decorated_function)

Type 2

This merely tears out the usage of decorators, and just a slight tweak.What if we don't return , as well not use callable object at all.

def decorator_func(to_be_decorated_function):
    print("Logging IN: Currrently  in function")
    to_be_decorated_function()
    print("Logging OUT: Currrently  in function")

@decorator_func
def to_be_decorated_function():
    print('October 16, 2000')

to_be_decorated_function  # notice I'm just using the object and not callable function
# equivalent to
#decorator_func(to_be_decorated_function)

Output from both the types

Logging IN: Currrently  in function
October 16, 2000
Logging OUT: Currrently  in function


来源:https://stackoverflow.com/questions/45335580/why-do-we-need-wrapper-function-in-decorators

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!