How can attributes on functions survive wrapping?

走远了吗. 提交于 2019-12-13 02:38:07

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!