mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-11-27 04:44:08 +03:00
[python/en] Updated Decorator and wrapping explanation (#4749)
Now it includes motivation, an explanation of functools.wraps, and demonstrates the utility of wrapping.
This commit is contained in:
parent
62b5f91d72
commit
7b2491ecd5
@ -1016,31 +1016,67 @@ gen_to_list = list(values)
|
|||||||
print(gen_to_list) # => [-1, -2, -3, -4, -5]
|
print(gen_to_list) # => [-1, -2, -3, -4, -5]
|
||||||
|
|
||||||
|
|
||||||
# Decorators
|
# Decorators are a form of syntactic sugar.
|
||||||
# In this example `beg` wraps `say`. If say_please is True then it
|
# They make code easier to read while accomplishing clunky syntax.
|
||||||
# will change the returned message.
|
|
||||||
from functools import wraps
|
|
||||||
|
|
||||||
|
# Wrappers are one type of decorator.
|
||||||
|
# They're really useful for adding logging to existing functions without needing to modify them.
|
||||||
|
|
||||||
def beg(target_function):
|
def log_function(func):
|
||||||
@wraps(target_function)
|
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
msg, say_please = target_function(*args, **kwargs)
|
print("Entering function", func.__name__)
|
||||||
if say_please:
|
result = func(*args, **kwargs)
|
||||||
return "{} {}".format(msg, "Please! I am poor :(")
|
print("Exiting function", func.__name__)
|
||||||
return msg
|
return result
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
@log_function # equivalent:
|
||||||
|
def my_function(x,y): # def my_function(x,y):
|
||||||
|
return x+y # return x+y
|
||||||
|
# my_function = log_function(my_function)
|
||||||
|
# The decorator @log_function tells us as we begin reading the function definition
|
||||||
|
# for my_function that this function will be wrapped with log_function.
|
||||||
|
# When function definitions are long, it can be hard to parse the non-decorated
|
||||||
|
# assignment at the end of the definition.
|
||||||
|
|
||||||
@beg
|
my_function(1,2) # => "Entering function my_function"
|
||||||
def say(say_please=False):
|
# => "3"
|
||||||
msg = "Can you buy me a beer?"
|
# => "Exiting function my_function"
|
||||||
return msg, say_please
|
|
||||||
|
|
||||||
|
# But there's a problem.
|
||||||
|
# What happens if we try to get some information about my_function?
|
||||||
|
|
||||||
|
print(my_function.__name__) # => 'wrapper'
|
||||||
|
print(my_function.__code__.co_argcount) # => 0. The argcount is 0 because both arguments in wrapper()'s signature are optional.
|
||||||
|
|
||||||
|
# Because our decorator is equivalent to my_function = log_function(my_function)
|
||||||
|
# we've replaced information about my_function with information from wrapper
|
||||||
|
|
||||||
|
# Fix this using functools
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
def log_function(func):
|
||||||
|
@wraps(func) # this ensures docstring, function name, arguments list, etc. are all copied
|
||||||
|
# to the wrapped function - instead of being replaced with wrapper's info
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
print("Entering function", func.__name__)
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
print("Exiting function", func.__name__)
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
@log_function
|
||||||
|
def my_function(x,y):
|
||||||
|
return x+y
|
||||||
|
|
||||||
|
my_function(1,2) # => "Entering function my_function"
|
||||||
|
# => "3"
|
||||||
|
# => "Exiting function my_function"
|
||||||
|
|
||||||
|
print(my_function.__name__) # => 'my_function'
|
||||||
|
print(my_function.__code__.co_argcount) # => 2
|
||||||
|
|
||||||
print(say()) # Can you buy me a beer?
|
|
||||||
print(say(say_please=True)) # Can you buy me a beer? Please! I am poor :(
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Free Online
|
### Free Online
|
||||||
|
Loading…
Reference in New Issue
Block a user