Патч обезьяны с частичной функцией

Я пытаюсь 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)