Декорирование метода класса вызывает несвязанный метод TypeError в 2.7 - PullRequest
2 голосов
/ 09 июля 2019

Всякий раз, когда я декорирую метод класса вне определения класса и использую его, он выдает мне TypeError, говорящую о несвязанном методе с экземпляром класса в качестве первого аргумента.

Я возвращаю декорированный метод, используя setattr () метод.Например:

class A(object):

    @classmethod
    def demo_method(cls, a):
        print a

def decorator(function):

    from functools import wraps
    @wraps(function)
    def wrapper(*args, **kwargs):
        return_value = function(*args, **kwargs)
        return return_value

    return wrapper

setattr(A, 'demo_method', decorator(A.demo_method))

A.demo_method(1)

Выдает следующую ошибку:

TypeErrorTraceback (most recent call last)
<ipython-input-13-447ee51677f4> in <module>()
     17 setattr(A, 'demo_method', decorator(A.demo_method))
     18 
---> 19 A.demo_method(1)
     20 

TypeError: unbound method demo_method() must be called with A instance as first argument (got int instance instead)

1 Ответ

1 голос
/ 10 июля 2019

Поведение, которое вы видите, на самом деле не связано с использованием самого декоратора.Вы также можете сказать следующее:

class C(object):
    pass

setattr(C, 'm', lambda a: a)  # actually also just: C.m = lambda a: a
print(C.m(1))

И вы все равно получите по существу ту же ошибку.Вы можете прочитать немного больше в документации по Python 2 для пользовательских методов , а именно этих битов:

Обратите внимание, что преобразование из объекта функции в (несвязанный или связанный) методОбъект происходит каждый раз, когда атрибут извлекается из класса или экземпляра.... Также обратите внимание, что это преобразование происходит только для пользовательских функций;другие вызываемые объекты (и все не вызываемые объекты) извлекаются без преобразования.Также важно отметить, что пользовательские функции, которые являются атрибутами экземпляра класса, не преобразуются в связанные методы;это происходит только тогда, когда функция является атрибутом класса.

И исключение, которое вы выполняете, потому что:

Когда вызывается несвязанный объект пользовательского методавызывается базовая функция (im_func) с ограничением на то, что первый аргумент должен быть экземпляром соответствующего класса (im_class) или его производного класса.

Если вы присмотрелесьC.m, вы увидите:

>>> print(C.m)
<unbound method C.<lambda>>
>>> print(C().m)
<bound method C.<lambda> of <__main__.C object at 0x7f6f6c5fa850>>

Принимая во внимание, что с Python 3 (как было указано в комментарии, эта конструкция прошла бы), поведение будет:

>>> print(C.m)
<function <lambda> at 0x7f69fbe8d0d0>
>>> print(C().m)
<bound method <lambda> of <__main__.C object at 0x7f69fbe89940>>

Примечание C.m в последнем случае все еще используется как (простая) функция, а не как (несвязанный) метод.

Я не совсем уверен, для чего именно вы в конечном итоге и в каком направлении будете наиболее полезны, но чтобы получить то же поведение, что и в Python 3 для этого кода, вы можете изменить (не то, чтобы я рекомендовал это) C.m() вызов C.m.__func__(), чтобы получить прямой доступ к базовой функции.Или с вашим примером:

A.demo_method.__func__(1)
...