Catalog
Returns a function from a function
Pass a function as an argument to another function
Your first decorator
You don't have to execute another function in one function , We can also return it as output :
def hi(name="yasoob"):
def greet():
return "now you are in the greet() function"
def welcome():
return "now you are in the welcome() function"
if name == "yasoob":
return greet
else:
return welcome
a = hi()
print(a)
#outputs: <function greet at 0x7f2143c01500>
# It clearly shows `a` Now point to hi() Function greet() function
# Now try this
print(a())
#outputs: now you are in the greet() function
Look at this code again . stay if/else In statement we return greet and welcome, instead of greet() and welcome(). Why is that ? This is because when you put a pair of parentheses at the end , This function will execute ; However, if you don't put the parentheses after it , Then it can be passed around , And you can assign values to other variables without executing them .
Do you understand ? Let me explain a little more details .
When we write down a = hi(),hi() Will be performed , And because the name The default parameter is yasoob, So letter
Count greet Be returned to the . If we change the statement to a = hi(name = "ali"), that welcome The function will be returned . We can also print it out hi()(), This output now you are in the greet() function.
def hi():
return "hi yasoob!"
def doSomethingBeforeHi(func):
print("I am doing some boring work before executing hi()")
print(func())
doSomethingBeforeHi(hi)
#outputs:I am doing some boring work before executing hi()
# hi yasoob!
Now you have all the necessary knowledge , To learn more about what a decorator really is . Decorators let you execute code before and after a function .
In the last example , In fact, we have created a decorator ! Now let's modify the next decorator , And write a slightly more useful program :
def a_new_decorator(a_func):
def wrapTheFunction():
print("I am doing some boring work before executing a_func()"
a_func()
print("I am doing some boring work after executing a_func()"
return wrapTheFunction
def a_function_requiring_decoration():
print("I am the function which needs some decoration to remove my foul smell")
a_function_requiring_decoration()
#outputs: "I am the function which needs some decoration to remove my foul smell
a_function_requiring_decoration=a_new_decorator(a_function_requiring_decoration)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()
a_function_requiring_decoration()
#outputs:I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()
Do you understand ? We just applied the principles we learned before . That's exactly what it is. python What the decorator does ! They encapsulate a function , And modify its behavior in one way or another . Now you may wonder , We don't use... In the code @ Symbol ? That's just a short way to generate a decorated function . Here's how we use @ To run the previous code :
@a_new_decorator
def a_function_requiring_decoration():
"""Hey you! Decorate me!"""
print("I am the function which needs some decoration to remove my foul smell")
a_function_requiring_decoration()
#outputs: I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()
#the @a_new_decorator is just a short way of saying:
a_function_requiring_decoration=a_new_decorator(a_function_requiring_decoration)
I hope you are right now Python There is a basic understanding of the working principle of the decorator . If we run the following code, there will be a problem :
print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction
That's not what we want !Ouput The output should be “a_function_requiring_decoration”. The function here is defined by warpTheFunction Replaced the . It overrides the name and comment documentation of our function (docstring). Fortunately, Python Give us a simple function to solve this problem , That's it functools.wraps. Let's modify the previous example to use it
functools.wraps:
from functools import wraps
def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
print("I am doing some boring work before executing a_func()"
a_func()
print("I am doing some boring work after executing a_func()"
return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
"""Hey yo! Decorate me!"""
print("I am the function which needs some decoration to remove my foul smell")
print(a_function_requiring_decoration.__name__)
# Output: a_function_requiring_decoration
It's much better now . Let's learn some common scenes of decorators .
Blueprint specification :
from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args, **kwargs):
if not can_run:
return "Function will not run"
return f(*args, **kwargs)
return decorated
@decorator_name
def func():
return("Function is running")
can_run = True
print(func())
# Output: Function is running
can_run = False
print(func())
# Output: Function will not run
Be careful :@wraps Accept a function to decorate , And added the copy function name 、 Annotation document 、 Parameter list and so on . This allows us to access the properties of the function before the decoration in the decorator .