cls在装饰类的继承classmethod中的行为

时间:2017-02-16 10:54:07

标签: python inheritance python-decorators class-method

我试图使用调用它们时使用的一个参数对类的类方法进行一些验证。

为此,我使用了一个装饰器,用于将装饰器应用于所需的方法,该方法将使用函数中的一个参数执行验证功能。

这一切都适用于基类(对于此示例,我将其称为Parent)。

但是,如果我创建另一个继承Parent的类,(对于此示例,我将其称为Child),继承的装饰classmethod将不再正常运行。

cls类的classmethod中的Child参数不是Child符合预期,而是Parent

采用以下示例

import inspect


def is_number(word):
    if word.isdigit():
        print('Validation passed')
    else:
        raise Exception('Validation failed')


class ClassDecorator(object):

    def __init__(self, *args):
        self.validators = args

    def __decorateMethod(self):
        def wrapped(method):
            def wrapper(cls, word, *args, **kwargs):
                for validator in self.validators:
                    validator(word)
                return method(word, *args, **kwargs)
            return wrapper
        return wrapped

    def __call__(self, cls):
        for name, method in inspect.getmembers(cls):
            if name == 'shout':
                decoratedMethod = self.__decorateMethod()(method)
                setattr(cls, name, classmethod(decoratedMethod))
        return cls


@ClassDecorator(is_number)
class Parent(object):

    @classmethod
    def shout(cls, word):
        print('{} is shouting {}'.format(cls, word))

    @classmethod
    def say(cls):
        print('{} is talking'.format(cls))


class Child(Parent):
    pass


Parent.shout('123')
Child.shout('321')

将导致以下输出:

Validation passed
<class '__main__.Parent'> is shouting 123
Validation passed
<class '__main__.Parent'> is shouting 321

我的问题是:

  • 为什么使用Child调用Parent的类方法作为cls
  • 是否可以使用此设计来获得想要的行为?

P.S。:我已经在Python 2.7.10和Python 3.5.2上尝试了这个并且已经有了相同的行为

1 个答案:

答案 0 :(得分:6)

您正在装饰绑定类方法;正是这个对象持有Parent并在调用时将其传递给原始shout函数; <{1}}方法中绑定的任何cls都不会被传入和忽略。

首先打开classmethods,你可以使用wrapper()属性访问底层函数对象:

__func__

您现在必须考虑到您的包装器也在处理未绑定的函数,因此传递def __call__(self, cls): for name, method in inspect.getmembers(cls): if name == 'shout': decoratedMethod = self.__decorateMethod()(method.__func__) setattr(cls, name, classmethod(decoratedMethod)) return cls 参数或手动绑定:

cls