Сочетание functools.partialmethod и classmethod

Я хотел бы использовать functools.partialmethod на уроке метод. Однако поведение, которое я нахожу, не то, что я ожидал (и хотел бы иметь). Вот пример:

class A(object):
    @classmethod
    def h(cls, x, y):
        print(cls, x, y)

class B(A):
    h = functools.partialmethod(A.h, "fixed")

Когда я делаю

>>> b = B()
>>> b.h(3)

Я получаю ошибку:

...
TypeError: h() takes 3 positional arguments but 4 were given

Это согласуется с

>>> b.h()
<class '__main__.A'> <__main__.B object at 0x1034739e8> fixed

Однако я ожидал (и хотел бы иметь) следующее поведение:

>>> b.h(4)
<class '__main__.B'> fixed 4

я думаю что functools.partialmethod лечит B.h как обычный метод экземпляра и автоматически передает фактический экземпляр в качестве первого аргумента. Но это поведение делает functools.partialmethod бесполезно для замораживания аргументов в методах классов наследующих классов.

2 ответа

Не вдаваясь в подробности, partial объект плохо сочетается с протоколом дескриптора, который @classmethod использует для создания экземпляра класса. Простое решение - просто определить переопределенный метод обычным способом:

class B(A):
    @classmethod
    def h(cls, y):
        return A.h("fixed", y)

Может быть возможно сделать то, что вы хотите с некоторым вызовом partial, но я не смог его найти. Вот некоторые из моих попыток, и почему они потерпели неудачу.

A.h вызывает __get__ метод объекта функции, возвращающий функцию, в которой первый аргумент уже связан с вызывающим классом. partial затем применяет эту функцию к "fixed", но затем вызываемый вызываемый элемент по- прежнему имеет __get__ метод, который пытается вставить вызывающий класс в результирующий вызов. Вы можете попытаться обойти это, определив h на самом деле быть статическим методом:

class B(A):
    h = staticmethod(partial(A.h, "fixed"))

>>> B.h(4)
<class '__main__.A'> fixed 4

Но, как вы видите, вы уже заморозили аргумент класса, когда вызываете partial, Другая попытка - избежать протокола дескриптора путем прямого доступа к аргументу:

class B(A):
    h = staticmethod(partial(A.__dict__["h"], "fixed"))

но classmethod объекты на самом деле не могут быть вызваны; только возвращаемое значение их __get__ методы есть, поэтому вызов partial выходит из строя.

Использование super (V2.7)

class A(object):
    @classmethod
    def c(cls, x, y):
        print('cls:{}, x:{}, y:{}'.format(cls, x, y))

class B(A):
    def c(self, y):
        super(B, self).c(x = 'fixed', y = y) 

class C(A):
    def c(self, x):
        super(C, self).c(x = x, y = 'fixed') 

b = B()
c = C()

>>> 
>>> b.c(4)
cls:<class '__main__.B'>, x:fixed, y:4
>>> c.c(4)
cls:<class '__main__.C'>, x:4, y:fixed
>>> 

супер это супер

Другие вопросы по тегам