Как использовать декоратор для привязки аргумента к функции 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