Как класс украсить метод класса - PullRequest
0 голосов
/ 25 июня 2019

Я использую декоратор класса для подкласса TestClass, который наследует SuperClass.У меня есть метод класса в SuperClass, который называется что (CLS), который принимает в классе.Я хочу иметь возможность украсить этот класс в моем подклассе TestClass, но он не позволяет мне, как говорится.

TypeError: unbound method wrapper() must be called with TestClass instance as first argument (got nothing instead)

Я попытался создать экземпляр моего TestClassобъект и затем использовать это для вызова метода testclass.what(cls), и это работает, но когда я делаю TestClass.what(), это дает мне ошибку выше.

def class_decorator(cls):
    for attr_name in dir(cls):
        attr_value = getattr(cls, attr_name)
        if hasattr(attr_value, '__call__'):  # check if attr is a function
            # apply the function_decorator to your function
            # and replace the original one with your new one
            setattr(cls, attr_name, ball(attr_value))
    return cls


def ball(func):
    def wrapper(*args, **kwargs):
        print("hello")
        return func(*args, **kwargs)

    return wrapper



class SuperClass:
    def __init__(self):
        pass

    @classmethod
    def what(cls):
        print("testing")


@class_decorator
class TestClass(SuperClass):

    def what(cls):
        super().what()


TestClass.what()

Ожидается:

"hello"
"testing"
"hello"
"testing"

Actual: TypeError: unbound method wrapper() must be called with TestClass instance as first argument (got nothing instead)

1 Ответ

0 голосов
/ 25 июня 2019

Есть несколько проблем с вашей программой; вместо того, чтобы вдаваться во все детали, я укажу способ получить желаемый результат.

Ошибка возникает из-за того, что вы переопределили метод what в TestClass, который теперь принимает единственный аргумент cls, который может быть чем угодно. Другими словами, вам также необходимо украсить ваш метод класса в подклассе с помощью декоратора classmethod.

Если вы хотите сохранить текущий код, вам нужно предоставить аргумент cls точно, что-то вроде следующего должно сделать:

from functools import partial

def class_decorator(cls): 
    for attr_name in vars(cls): 
        attr_value = getattr(cls, attr_name) 
        if callable(attr_value):  # check if attr is a function 
            if attr_name == 'what': 
                setattr(cls, attr_name, partial(ball(attr_value), cls=cls)) 
            else: 
                setattr(cls, attr_name, ball(attr_value)) 

с использованием partial для передачи класса в качестве первого аргумента.

Кроме того, я использовал vars(cls), чтобы получить cls.__dict__, поскольку dir выполняет рекурсию по базам (чего вы здесь не хотите). Кроме того, вместо проверки атрибута __call__ используйте callable.

...