[Tutor] Writing decorators?

Steven D'Aprano steve at pearwood.info
Wed Jul 20 14:54:33 EDT 2016


On Wed, Jul 20, 2016 at 03:30:43PM +0200, Michael Welle wrote:

> > It [a decorator]
> > can modify the function and return it,
>
> Now it gets interesting ;). Can you give me a hint on how to modify the
> code of the function in a decorator or even give a small example,
> please? Would I take the route with the bytecode attribute __code__
> (IIRC)? Or use the inspect module? 

The inspect module is not really designed for changing objects, only for 
inspecting them. (Reading, not writing.)

Function code objects are immutable, so you cannot change them in place, 
only replace them with a new code object:

py> def f():
...     print("f")
...
py> def g():
...     print("g")
...
py> f()
f
py> f.__code__ = g.__code__
py> f()
g


The code object itself is very complex, and badly documented:

py> help(f.__code__)

Help on code object:

class code(object)
 |  code(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring,
 |        constants, names, varnames, filename, name, firstlineno,
 |        lnotab[, freevars[, cellvars]])
 |
 |  Create a code object.  Not for the faint of heart.


so in practice the way to create them is by actually defining a 
function, then extracting its __code__ object. But if you're going to do 
that, why not just use the function?

There are a couple of projects designed to let you manipulate the 
byte-code of functions, but because the byte-code format is not part of 
the public Python API, it tends to change from version to version. If 
you use the wrong byte-code, you can crash the interpeter and cause a 
segmentation fault or core dump.

However, there is some talk about creating a interface to modify a 
function's abstract syntax tree. At the moment consider that to be just 
talk, but its more likely than a supported interface to edit byte-code. 

But for those brave, or silly, enough, here are some resources for 
editing byte-code to get you started:

http://www.voidspace.org.uk/python/articles/code_blocks.shtml
https://wiki.python.org/moin/ByteplayDoc


There are ways to modify functions apart from changing their code. For 
example, you can change argument defaults, add attributes to the 
function object, change the global namespace that the function works 
with. I have an experimental project that modifies functions so that 
instead of searching for variables in this order:

locals
nonlocals
globals
builtins


it uses:

locals
nonlocals
custom namespace set by the user
globals
builtins

(Technically, I don't "modify" the function, since some parts of the 
function are immutable and cannot be changed. Instead I replace it with 
a new function, copied from the original but with a slight 
modification.)


If you are interested in that, see the discussion that starts here:

https://mail.python.org/pipermail/python-list/2016-July/711177.html

The original version of the code I gave uses a metaclass; the version I 
have now also supports being called as a decorator.



-- 
Steve


More information about the Tutor mailing list