如何使用装饰器将参数绑定到静态方法函数?

How to use decorator to bind a argument to a staticmethod function?

我的问题如下所示:

class foo(obj):

    def __init__(self, st='123'):
       self.st = st

    def process(self, x):
       self.st += x

    @staticmethod
    def do_foo(x, myfoo=None):
        if myfoo is None:
           myfoo = foo()
        myfoo.process(x)

def wrapper(fn, st):

    foo_func = foo(st)
    foo.do_foo = functools.partial(foo.do_foo, myfoo=foo_func)
    fn()
    print foo_func.st

    return wrap

@wrapper('stst')
def pro():
    foo.do_foo('double')

def pro2():
    foo.do_foo('double')

pro2()   # <--- normal foo.do_foo
pro()    # <--- partialed foo.do_foo
pro2()   # <--- partialed foo.do_foo

我想创建 wrapper 装饰器来包装带有自定义 foo class 的静态方法 foo.do_foo,并且在 pro() 执行后,这个装饰器是能够跨越 foo 对象来做一些工作。即保存变量值。

在上面的代码中,包装器如何在全局范围内永久更改 foo.do_foo,而不仅仅是在装饰器范围内更改它。

那么如何让 foo.do_foo 只在装饰器范围内改变而不是全局的?

根据您评论中链接的 gist.github.com code,这里有一个略有不同的答案(似乎是基于我的答案的第一个版本)。

正如我最初所说,在我看来你需要做的是使 wrapper() 成为一个 装饰器工厂 函数而不是装饰器本身——换句话说使它成为一个函数,根据它的参数创建和 returns 一个装饰器。

正如我在对您的评论的回复中提到的,问题是 Prof.do staticmethod 本质上是一个全局变量,如果包装函数更改它,这将影响所有后续对它的调用——这是问题的根本原因。

一个解决方法是让装饰器创建的 wrapped() 函数在调用装饰函数之前保存 Prof.do 的值,然后在之后恢复它,因此更改只会影响对该函数的调用虽然它。这可以防止它对 Prof.do 所做的事情弄乱其他可能创建的包装函数中对它的其他调用。它还可以防止它们的效果累积。

我将更改和恢复静态方法封装在 contextmanager 辅助函数中。需要这样做的一个缺点是它会增加调用包装函数所涉及的开销。

import contextlib
import functools

class Prof(object):
    def __init__(self, p='s'):
        self.p = p

    @staticmethod
    def do(x, obj=None):
        if obj is None:
            obj = Prof()
        obj.dprint(x)
        print

    def dprint(self, x):
        print self.p, x
        self.p += x

def wrapper(st):
    @contextlib.contextmanager
    def prof_context(obj):  # could also be defined outside of wrapper function
        # save current staticmethod and replace it with partial below
        saved_method, Prof.do = Prof.do, functools.partial(Prof.do, obj=obj)
        yield
        # undo staticmethod modification
        Prof.do = staticmethod(saved_method)

    def decorator(fn):
        @functools.wraps(fn)
        def wrapped():
            obj = Prof(st)
            print 'current: obj.p is %r' % obj.p
            with prof_context(obj):
                fn()

        return wrapped

    return decorator

def do_p():
    Prof.do('do')

@wrapper('do_p2')
def do_p2():
    Prof.do('do2')

print '#A do_p():'
do_p()
print '#B do_p2():'
do_p2()
print '#C do_p():'
do_p()
print '#D do_p2():'
do_p2()
print '#E do_p():'
do_p()
print '#F do_p():'
do_p()

输出:

#A do_p():
s do

#B do_p2():
current: obj.p is 'do_p2'
do_p2 do2

#C do_p():
s do

#D do_p2():
current: obj.p is 'do_p2'
do_p2 do2

#E do_p():
s do

#F do_p():
s do