Сочетание 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
>>>