Мне нужно сгенерировать класс, который бы имитировал набор методов другого класса и вел себя как последний через прокси. например Если Base
класс, чтобы имитировать и Deleguate
класс, который должен действовать как Base
то:Использование закрытия для украшения методов класса
b = Base(args)
b.any_function()
строго эквивалентно
d = Deleguate(b)
d.any_function()
Если Deleguate
использует функцию, которая уже существует в Base
, то не будет перезаписана. Это поведение, которое вы ожидаете от наследования и переопределения метода. Наследование не является вариантом в контексте проекта, над которым я работаю (среди других ограничений у меня нет доступа к заводскому коду). И это осложняет ситуацию.
поэтому я решил закодировать «прокси» декоратор:
import inspect
def proxy(bridge, target):
def proxyfy(cls):
for _, func in inspect.getmembers(target, predicate=inspect.ismethod):
fname = func.__name__
if fname in cls.__dict__:
print 'ignoring %s.%s' % (cls, fname)
continue
print 'adding %s.%s' % (cls, fname)
def proxy_func(self, *args, **kwargs):
print 'calling %s.%s.%s' % (cls, bridge, fname)
bridge_member = getattr(self, bridge)
return getattr(bridge_member, fname)(*args, **kwargs)
setattr(cls, fname, proxy_func)
return cls
return proxyfy
class Base(object):
def __init__(self, i):
self._i = i
def __bar(self):
print 0
def foo(self):
print self._i
def foo2(self):
print 2 * self._i
@proxy('_proxy', Base)
class Deleguate(object):
def __init__(self, base):
self._proxy = base
def foo2(self):
print 4 * self._proxy._i
d = Deleguate(Base(1))
d.__bar() # d._proxy.__bar()
d.foo() # d._proxy.foo()
d.foo2() # d.foo2()
я получаю следующий результат:
adding <class '__main__.Deleguate'>.__bar
ignoring <class '__main__.Deleguate'>.__init__
adding <class '__main__.Deleguate'>.foo
ignoring <class '__main__.Deleguate'>.foo2
calling <class '__main__.Deleguate'>._proxy.foo2
2
calling <class '__main__.Deleguate'>._proxy.foo2
2
4
Я думал, что setattr(cls, fname, proxy_func)
назначит новое замыкание, но аргументы будут перезаписаны на каждом шаге цикла и сохраняются только аргументы последней функции foo2
. Поэтому при вызове любой «сгенерированной» функции от Deleguate
используются аргументы foo2
...
Почему переменные замыкания перезаписываются? Есть ли способ создать такой прокси-код? Ожидаемый результат:
adding <class '__main__.Deleguate'>.__bar
ignoring <class '__main__.Deleguate'>.__init__
adding <class '__main__.Deleguate'>.foo
ignoring <class '__main__.Deleguate'>.foo2
calling <class '__main__.Deleguate'>._proxy.__bar
0
calling <class '__main__.Deleguate'>._proxy.foo
1
4
'fname' в' proxy_func() 'является * закрытием *; он не просматривается до тех пор, пока вы не назовете 'proxy_func()', после чего его значение по-прежнему привязано к последнему значению (в этом случае будет 'foo2') **, а не ** значение, с которым оно было связано, когда вы создали вложенная функция. –
Обходной путь - создать локальную переменную где-нибудь, чтобы связать значение в цикле; отдельная фабричная функция сделает это или предоставив 'proxy_func()' аргумент ключевого слова, который связывает 'fname' по умолчанию с объектом функции. –
@MartijnPieters Я сделал это. Но он терпит неудачу с 'AttributeError: 'Base' объект не имеет атрибута '__bar'' на' d .__ bar() # d._proxy .__ bar() '. : '( – thefourtheye