简体   繁体   English

如何包装(猴子补丁)@classmethod

[英]How to wrap (monkey patch) @classmethod

I want to monkey patch one single classmethod, keeping old functionality. 我想修补一个单一的类方法,保持旧的功能。 Consider my code to get the idea. 考虑一下我的代码来获取想法。 Here is my code (pretty synthetic example). 这是我的代码(非常合成的例子)。

#!/usr/bin/env python

class A:

  @classmethod
  def foo(kls, param):
    print 'A.foo called, param is ' + param

  def bar(self, param):
    print 'A.bar called, param is ' + param


a = A()
a.foo('param_foo')
a.bar('param_bar')

# Patching things

def bar_wrapper(wrapped_func):
  def _w(*args, **kwargs):
    print '<bar_wrap>'
    wrapped_func(*args, **kwargs)
    print '</bar_wrap>'
  return _w

def foo_wrapper(wrapped_func):
  # Something missing here?
  def _w(*args, **kwargs):
    print '<foo_wrap>'
    wrapped_func(*args, **kwargs)
    print '</foo_wrap>'
  return _w

# Everything is pretty ok
A.bar = bar_wrapper(A.bar)
a.bar('is_is_wrapped?')

# Failed to wrap @classmethod
A.foo = foo_wrapper(A.foo)
A.foo('another_wrap_test')

This is what I expect to output: 这是我期望输出的:

A.foo called, param is param_foo
A.bar called, param is param_bar
<bar_wrap>
A.bar called, param is is_is_wrapped?
</bar_wrap>
<foo_wrap>
A.foo called, param is another_wrap_test
</foo_wrap>

And this is what I get: 这就是我得到的:

A.foo called, param is param_foo
A.bar called, param is param_bar
<bar_wrap>
A.bar called, param is is_is_wrapped?
</bar_wrap>
Traceback (most recent call last):
  File "./pytest.py", line 39, in <module>
    A.foo('another_wrap_test')
TypeError: unbound method _w() must be called with A instance as first argument (got str instance instead)

Seems like one parameter (class-param) was lost during wrapping. 好像在包装过程中丢失了一个参数(class-param)。 Or I just don't the idea of decorating functions? 或者我只是没有装饰功能的想法?

Thanks in advance. 提前致谢。

When you access a method on a class, it is wrapped at that moment ; 当你访问一个类的方法时,它就被包裹 ; methods act as descriptors here. 方法在这里充当描述符

You may want to unwrap the method again, returning a wrapped wrapper: 您可能想要再次打开该方法,返回一个包装的包装器:

def foo_wrapper(wrapped_func):
    wrapped_func = wrapped_func.__func__
    def _w(*args, **kwargs):
        print '<foo_wrap>'
        wrapped_func(*args, **kwargs)
        print '</foo_wrap>'
    return classmethod(_w)

Now the returned decorator is itself a class method, and wrapping works: 现在返回的装饰器本身就是一个类方法,包装工作:

>>> class A:
...     @classmethod
...     def foo(kls, param):
...         print 'A.foo called, param is ' + param
... 
>>> def foo_wrapper(wrapped_func):
...     wrapped_func = wrapped_func.__func__
...     def _w(*args, **kwargs):
...         print '<foo_wrap>'
...         wrapped_func(*args, **kwargs)
...         print '</foo_wrap>'
...     return classmethod(_w)
... 
>>> A.foo = foo_wrapper(A.foo)
>>> A.foo('bar')
<foo_wrap>
A.foo called, param is bar
</foo_wrap>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM