【问题标题】:How can attributes on functions survive wrapping?函数的属性如何在包装中幸存下来?
【发布时间】:2016-09-08 15:07:19
【问题描述】:

假设我有以下函数,该函数具有一个属性,该属性将其标记为在回调子系统中进行特殊处理:

def my_func(msg):
    print msg

my_func.my_marker = SPECIAL_CONSTANT

问题是,如果其他代码位使用functools.partial 或其他装饰器包装my_func,那么my_marker 将会丢失。

my_partial = partial(my_func, 'hello world')
print my_partial.my_marker
>>> AttributeError...

有没有办法在包装时保护函数的属性?有没有更好的方法来存储我当前存储在my_marker 中的元数据?似乎存储对原始函数的引用也遇到了同样的问题。

【问题讨论】:

  • my_marker 没有丢失。它仍然在你放置它的地方,在原始函数上。
  • @kindall -- 谢谢,虽然这不是我的意思:) 问题是:我应该把函数元数据放在哪里,以便装饰器等在包装函数中维护属性?
  • 您可能想要做的是猴子补丁functools.partial 和/或functools.wraps 将这些属性(或者通常只是所有非下划线属性)复制到包装函数。

标签: python python-decorators


【解决方案1】:

如果你知道你使用的partial实际上是partial,你可以使用它的func属性。

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

如果你真的需要处理 meta_data,也许编写类并覆盖 __call__() 方法是一种更好的方法。

【讨论】:

  • 是的,使用__call__() 可能是这里要做的事情。
  • 对不起,我收回了 :( __call__() 在某种意义上是另一个属性,并且在被包裹时也会受到阴影的影响。
  • @matthewatabet 实际上,obo 拥有它的权利。定义__call__() 的自定义类——正如this answer elsewhere 所展示的——是大多数函数元数据问题的规范解决方案。这个问题也不例外。由于自定义类接受修饰函数作为其__init__() 方法的第一个参数,因此保留函数属性可以简单地简化为将这些属性复制到当前类实例中。这留给读者作为练习。 完成。
【解决方案2】:

这是另一个解决方案,使用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】:

    灵感来自 kindall 的评论、cbo 的回答和source code

    from functools import partial as _partial, update_wrapper
    
    def partial(func, *args, **keywords):
        return update_wrapper(_partial(func, *args, **keywords), func)
    

    一个显示它工作的例子和注意事项:

    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
    

    可以执行以下操作:

    import functools
    setattr(functools, 'partial', partial) # from above code
    

    但是,这可能是一个坏主意,因为 (1) 它需要在任何依赖它的代码之前被导入,(2) 它可能会破坏导入的代码,(3) 它可能混淆未来的人,(4) 将其保留在本地很容易。仅当您想强制第三方代码运行您的版本时才这样做。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-11
      • 1970-01-01
      相关资源
      最近更新 更多