问题
Let's say I have the following function which has an attribute which marks it for special handling within a callback subsystem:
def my_func(msg):
print msg
my_func.my_marker = SPECIAL_CONSTANT
The problem is that if other bits of code wrap my_func
with functools.partial
or another decorator, then my_marker
will be lost.
my_partial = partial(my_func, 'hello world')
print my_partial.my_marker
>>> AttributeError...
Is there a way to protect attributes on functions when wrapping? Is there a better way to store the metadata I'm currently storing in my_marker
? It seems like storing a reference to the original function suffers from the same problem.
回答1:
If you know that the partial you use is actually a partial, you can use it's func
attribute.
E. g.
from functools import partial
SPECIAL_CONSTANT = 'bam'
def my_func(msg):
print msg
my_func.my_marker = SPECIAL_CONSTANT
my_partial = partial(my_func, 'hello world')
print my_partial.func.my_marker
If you really have to handle meta_data, maybe it is a better approach to write classes and override the __call__()
method.
回答2:
Here is another solution, using functools.update_wrapper
.
from functools import partial, update_wrapper, WRAPPER_ASSIGNMENTS
SPECIAL_CONSTANT = 'bam'
def my_func(msg):
print msg
my_func.my_marker = SPECIAL_CONSTANT
my_partial = partial(my_func, 'hello world')
update_wrapper(my_partial, my_func, WRAPPER_ASSIGNMENTS + ('my_marker', ))
print my_partial.my_marker
回答3:
Inspired by kindall's comment, cbo's answer and the source code:
from functools import partial as _partial, update_wrapper
def partial(func, *args, **keywords):
return update_wrapper(_partial(func, *args, **keywords), func)
An example showing it working, and caveats:
def my_func(msg):
print msg
my_func.my_marker = 'FOO'
my_partial = partial(my_func, 'hello world')
print my_func.my_marker
print my_partial.my_marker
# check other stuff is still there
print my_partial.func
print my_partial.args
print my_partial.keywords
# this works fine for READ ONLY stuff.
# so just remember:
my_partial.my_marker = 'BAR' # this _only_ updates partial, not the original
print my_func.my_marker
print my_partial.my_marker
my_func.my_marker = 'BAZ' # this _only_ updates original, not the partial
print my_func.my_marker
print my_partial.my_marker
You can do something like:
import functools
setattr(functools, 'partial', partial) # from above code
However, this is probably a bad idea as (1) it would need to be imported before any code which relied on it, (2) it could break imported code, (3) it might confuse future people and (4) the alternative, keeping it locally, is easy. Only do this if you want to force third party code to run your version.
来源:https://stackoverflow.com/questions/37192712/how-can-attributes-on-functions-survive-wrapping