Патч обезьяны с частичной функцией
Я пытаюсь monkeypatch метод на SomeClass
из импортированного пакета:
from somepackage import SomeClass
def newmethod(obj, node, **kwargs):
""" """
SomeClass.oldmethod = newmethod
Где obj
и node
находятся в сигнатуре по умолчанию SomeClass.oldmethod
:
class SomeClass(object):
def oldmethod(obj, node):
""" """
Я знаю, что monkeypatching не является хорошей практикой, но нам нужно обходное решение, в то время как мы исправляем некоторые проблемы, которые иначе не могут быть решены. Вышеупомянутый подход работает FINE, но мы хотели бы использовать частичные функции для этого. Например:
from functools import partial
newmethod_a = partial(newmethod, foo='a')
newmethod_b = partial(newmethod, foo='b')
Частичная функция вызывается, потому что нам нужно передать разные ** kwargs. Но когда я пытаюсь перегрузить сейчас:
SomeClass.oldmethod = newmethod_a
Я получаю сообщение об ошибке, связанном с количеством переданных аргументов, но это очень специфично для моей проблемы, поэтому вставка может быть не очень полезной... Ошибка, которая, как мне кажется, связана с сигнатурой вызова oldmethod
с двумя позиционными аргументами (obj, node
), и мои частичные функции не передают ссылку на obj
и node
правильно. Я пробовал разные конструкции, например:
newmethod_a = partial(SomeClass.newmethod, foo='a')
Мне жаль, что я не могу создать минимальный рабочий пример. Я надеялся, что, возможно, эксперт просто узнает эту проблему из опыта и скажет мне, возможно ли, что я пытаюсь, в рамках partial
.
Спасибо
Ответы
Ответ 1
Вот пример:
from functools import partial
class foo(object):
def bar(self, pos1, **kwargs):
print("bar got self=%r, pos1=%r, kwargs=%r" % (self, pos1, kwargs))
foo.bar = partial(foo.bar, qux=1)
baz = foo()
baz.bar(1) # Fails...
Это не работает с TypeError
. Причиной этого является то, что baz.bar
является связанным методом, который ожидает, что его первый аргумент будет экземпляром foo
, но что объект partial
не является, и, следовательно, Python не будет добавлять self
для вас, когда вы вызываете baz.bar
. (Это не совсем правильно, но истинная причина довольно техническая. См. Описание дескриптора How-To ниже). Вызов baz.bar(baz, 1)
будет работать. Чтобы обойти это, вам нужно снова сделать метод foo.bar
:
import types
# In Python 2:
foo.bar = types.MethodType(partial(foo.bar.__func__, qux=1), None, foo)
# Method that is compatible with both Python 2 and 3:
foo.bar = types.MethodType(partial(foo.bar, qux=1), foo)
# Python 3 only:
from functools import partialmethod
foo.bar = partialmethod(foo.bar, qux=1)
baz = foo()
baz.bar(1) # Works!
См. также:
Ответ 2
Заменить:
newmethod_a = functools.partial(newmethod, foo='a')
с:
def newmethod_a(obj, node, **kwargs):
kwargs.update({"foo": "a"})
return newmethod(obj, node, **kwargs)