When I was looking at the pip repository on github, I found that I was using an interesting decorator, so I will share it.
TL;DR --Decorators can be used not only to qualify a function, but also to ** execute a function **. -[Main subject](Execute the function inside the #decorator)
First of all, what value do global variables take when importing the following packages? Both the functions spam
and ʻegg are functions only for updating the global variable
global_variable`.
mypackage/__init__.py
GLOBAL_VARIABLE = 0
def spam():
global_variable = 1
globals().update(locals())
def egg():
global global_variable
global_variable = 2
main.py
import mypackage
if __name__ == "__main__":
print(mypackage.global_variable)
The value of global_variable
hasn't changed since it was declared, because we're just defining the function and not executing it. Of course, the execution result is as follows.
$python3 main.py
0
So what if you try calling the function at the end of mypackage / __ init__.py
?
mypackage/__init__.py
GLOBAL_VARIABLE = 0
def spam():
global_variable = 1
globals().update(locals())
def egg():
global global_variable
global_variable = 2
spam()
egg()
In this case, the function is called and the value of global_variable
is overwritten in both spam
and ʻegg`, so the execution result is as follows.
$python3 main.py
2
However, with this method, if the number of functions you want to execute increases, you have to write the code to call that much, which is troublesome. That's where the ** decorator ** comes in.
Here is the main issue. Let's take a look at the code. Consider the following call_aside
function.
mypackage/__init__.py
global_variable = 0
def call_aside(f, *args, **kwargs):
f(*args, **kwargs)
return f
@call_aside
def spam():
global_variable = 1
globals().update(locals())
@call_aside
def egg():
global global_variable
global_variable = 2
main.py
import mypackage
if __name__ == "__main__":
print(mypackage.global_variable)
What the execution result is
$python3 main.py
2
It looks like! At first, I didn't understand the reason for this, so I was quite worried. However, the mechanism is simple once you understand it. Focusing on the call_aside
function,
def call_aside(f, *args, **kwargs):
f(*args, **kwargs) # <- execute f
return f
And so on, the function is called internally. Therefore, just by qualifying with call_aside
, spam
and ʻegg will be executed when mypackage is imported, so the value of
global_variable` has changed as shown above. In this case, even if the number of functions you want to execute at the time of import increases, you only have to decorate it: tada:
When examining Python decorators, many explain that decorators are used to ** "add processing before and after a function" and "return a function with processing added before and after" **. I think.
def decorator(f):
def wrapper(*args, **kwargs):
print("Preprocessing")
f(*args, **kwargs)
print("Post-processing")
return wrapper
In this way, the wrapper
function is just defined and not executed. Decorating a function with this decorator has no effect, such as updating the value of a global variable, until you call the decorated function.
pypa/pip/blob/master/src/pip/_vendor/pkg_resources/__init__.py
If you can read and write Python to some extent, take a look at official github such as CPython
and Pypa
. There are an infinite number of chords for the chords, so I think there will be new discoveries and helpful codes.
Recommended Posts