当前位置: 代码迷 >> python >> 如何使用装饰器将参数绑定到staticmethod函数?
  详细解决方案

如何使用装饰器将参数绑定到staticmethod函数?

热度:57   发布时间:2023-06-13 15:14:09.0

我的问题如下所示:

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类包装静态方法foo.do_foo ,并且在执行pro()之后,该装饰器能够跨foo对象执行某些工作。 即保存变量值。

在上层代码中,每个包装器如何在全局范围内永久地更改foo.do_foo ,而不仅仅是在装饰器范围内进行更改。

那么如何让foo.do_foo仅在装饰器作用域而不是全局范围内更改?

这是基于您评论中链接到的的稍有不同的答案(该似乎基于我的答案的第一个版本)。

就像我最初说的,在我看来,您需要做的是使wrapper()成为装饰器工厂函数,而不是装饰器本身-换句话说,使它成为一个根据其参数创建并返回装饰器的函数。 。

如我对您的评论的答复所述,问题是Prof.do staticmethod本质上是一个全局变量,如果包装的函数对其进行了更改,则这将影响所有对其的后续调用—这是问题的根本原因。

一种解决方法是使装饰器创建的Prof.do wrapped()函数在调用装饰的函数之前保存Prof.do的值,然后再将其还原,因此更改仅影响通过该函数进行的调用。 这样可以防止对Prof.do所做的Prof.do弄乱可能创建的其他包装函数中对它的其他调用。 这也防止了其效果的累积。

我已经将更改和恢复静态方法封装在辅助函数中。 需要执行此操作的缺点之一是,它增加了对包装函数的调用所涉及的开销。

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