问题
Say I have got some function fun
, the actual code body of which is out of my control. I can create a new function which does some preprocessing before calling fun
, i.e.
def process(x):
x += 1
return fun(x)
If I now want process
to take the place of fun
for all future calls to fun
, I need to do something like
# Does not work
fun = process
This does not work however, as this creates a cyclic reference problem as now fun
is called from within the body of fun
. One solution I have found is to reference a copy of fun
inside of process
, like so:
# Works
import copy
fun_cp = copy.copy(fun)
def process(x):
x += 1
return fun_cp(x)
fun = process
but this solution bothers me as I don't really know how Python constructs a copy of a function. I guess my problem is identical to that of extending a class method using inheritance and the super
function, but here I have no class.
How can I do this properly? I would think that this is a common enough task that some more or less idiomatic solution should exist, but I have had no luck finding it.
回答1:
Python is not constructing a copy of your function. copy.copy(fun)
just returns fun
; the difference is that you saved that to the fun_cp
variable, a different variable from the one you saved process
to, so it's still in fun_cp
when process
tries to look for it.
I'd do something similar to what you did, saving the original function to a different variable, just without the "copy":
original_fun = fun
def fun(x):
x += 1
return original_fun(x)
If you want to apply the same wrapping to multiple functions, defining a decorator and doing fun = decorate(fun)
is more reusable, but for a one-off, it's more work than necessary and an extra level of indentation.
回答2:
This looks like a use case for python's closures. Have a function return your function.
def getprocess(f):
def process(x):
x += 1
return f(x) # f is referenced from the enclosing scope.
return process
myprocess = getprocess(fun)
myprocess = getprocess(myprocess)
回答3:
Credit to coldspeed for the idea of using a closure. A fully working and polished solution is
import functools
def getprocess(f):
@functools.wraps(f)
def process(x):
x += 1
return f(x)
return process
fun = getprocess(fun)
Note that this is 100% equivalent to applying a decorator (getprocess
) to fun
. I couldn't come up with this solution as the dedicated decorator syntax @getprocess
can only be used at the definition place of the function (here fun
). To apply it on an existing function though, just do fun = getprocess(fun)
.
来源:https://stackoverflow.com/questions/52358965/python-redefine-function-so-that-it-references-its-own-self