6.2. Decorator Mechanics
6.2.1. Recap
When you define a function, Python will do several things
Python will create a function object
Python will compile function body to bytecode and assign it to the
funcobj.__code__.co_code
Python will add function name (identifier) to the
globals()
(name will be a reference of the function object)When you call a function, Python will call
funcobj.__call__()
method which will execute it's bytecode (funcobj.__code__.co_code
)
Definition:
>>> def hello():
... return 'hello'
Python will create a function object:
>>> hello
<function hello at 0x107f9e660>
Python will compile function body to bytecode and assign it
to the funcobj.__code__.co_code
:
>>> hello.__code__.co_code
b'\x95\x00g\x01'
Python will add function name (identifier) to the globals()
(name will be a reference of the function object):
>>> 'hello' in globals()
True
>>> globals()['hello']
<function hello at 0x107f9e660>
When you call a function, Python will call funcobj.__call__()
method which will execute it's bytecode (funcobj.__code__.co_code
):
>>> hello.__call__()
'hello'
6.2.2. Function Definition
Let's define a simple function:
>>> def hello():
... return 'hello'
Now we can call the function:
>>> hello()
'hello'
6.2.3. Create an Alias
Let's define a simple function:
>>> def hello():
... return 'hello'
Now we can call the function:
>>> hello()
'hello'
We can also define an alias to the function:
>>> alias = hello
>>> alias()
'hello'
Why this works:
>>> hello
<function hello at 0x107f9e660>
>>>
>>> alias
<function hello at 0x107f9e660>
6.2.4. Helper Function
debug content was executed at the moment of decorating the function (not calling it)
Let's define a simple function:
>>> def debug(func):
... print('debug')
... return func
>>>
>>> def hello():
... return 'hello'
Now we can call the function:
>>> hello()
'hello'
Creating alias
will execute extra steps:
>>> alias = debug(hello)
debug
>>>
>>> alias()
'hello'
Note, that the debug
content was executed at the moment of decorating
the function (not calling it). If you want to delay this to the moment of
a call, we need to introduce a one more step.
Both hello
and alias
points to the same object:
>>> hello
<function hello at 0x107f9e660>
>>>
>>> alias
<function hello at 0x107f9e660>
6.2.5. Delay Execution
We need to delay the execution of the debug function content
>>> def debug(func):
... def on_call():
... print('debug')
... return func()
... return on_call
>>>
>>> def hello():
... return 'hello'
Calling hello
works as usual:
>>> hello()
'hello'
Creating alias
will execute extra steps:
>>> alias = debug(hello)
>>> alias()
debug
'hello'
This time hello
and alias
points to the different object.
Identifier hello
points as usual to the function object, but
alias
points to the on_call
function object, which is a wrapper
around the original function. Upon a call, on_call
it will execute
the extra steps and then call the original function.
>>> hello
<function hello at 0x107f9e660>
>>>
>>> alias
<function debug.<locals>.on_call at 0x107f9c360>
6.2.6. Make New Behavior Permanent
If we define alias with the same name as the original function
globals()['hello']
will point not tohello()
but todebug()
functionThis will make the new behavior permanent
>>> def debug(func):
... def on_call():
... print('debug')
... return func()
... return on_call
>>>
>>> def hello():
... return 'hello'
Calling hello
works as usual:
>>> hello()
'hello'
>>>
>>> hello
<function hello at 0x107f9e660>
If we define alias with the same name as the original function,
the globals()['hello']
will point not to hello()
but
to debug()
function instead. This will make the new behavior
permanent. Whenever someone calls the hello()
he/she will
get a new behavior:
>>> hello = debug(hello)
>>> hello()
debug
'hello'
This is because the hello
identifier now points to the debug
:
>>> hello
<function debug.<locals>.on_call at 0x107f9c360>
6.2.7. Decorator Syntax
@debug
is just a shortcut tohello = debug(hello)
>>> def debug(func):
... def on_call():
... print('debug')
... return func()
... return on_call
>>>
>>> @debug
... def hello():
... return 'hello'
Calling hello
won't work as usual. This time it will execute
the on_call
body, adding few extra steps.
>>> hello()
debug
'hello'