Как использовать декоратор для привязки аргумента к функции staticmethod?

Моя проблема выглядит следующим образом:

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.do_foo с настройкой foo класс, а после pro() выполненный, этот декоратор сможет выполнить через объект foo некоторую работу. т.е. сохранить значение переменной.

Как каждый, в верхнем коде, обернуть изменить foo.do_foo навсегда глобально, а не просто изменить его в области декоратора.

Так как просто позволить foo.do_foo изменилось только в области декоратора не глобально?

1 ответ

Решение

Вот немного другой ответ, основанный на коде gist.github.com, указанном в вашем комментарии (который, похоже, основан на первой версии моего ответа).

Как я уже говорил, для меня это звучит так, как будто вам нужно сделать wrapper() функция фабрики декоратора, а не сам декоратор - другими словами, сделать ее функцией, которая создает и возвращает декоратор на основе его аргументов.

Как уже упоминалось в моем ответе на ваш комментарий, проблема в том, что 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
Другие вопросы по тегам