【问题标题】:cls behaviour in inherited classmethod of a decorated class装饰类的继承类方法中的 cls 行为
【发布时间】:2017-07-05 10:34:39
【问题描述】:

我正在尝试使用调用它们时使用的参数之一对类的类方法进行一些验证。

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

这一切都适用于基类(在本例中,我将其称为Parent)。

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

Child 类的 classmethod 中的 cls 参数不是预期的 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 的 classmethod 以 Parent as cls 调用
  • 是否有可能使用这种设计来获得想要的行为?

P.S.:我在 Python 2.7.10 和 Python 3.5.2 上都试过了,并且得到了相同的行为

【问题讨论】:

    标签: python inheritance python-decorators class-method


    【解决方案1】:

    你正在装饰绑定的类方法;正是这个对象保留了Parent,并在调用时将其传递给原始的shout 函数; cls 在您的 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
    

    您现在必须考虑到您的包装器也在处理 unbound 函数,因此请传递 cls 参数或手动绑定:

    # pass in cls explicitly:
    return method(cls, word, *args, **kwargs)
    
    # or bind the descriptor manually:
    return method.__get__(cls)(word, *args, **kwargs)
    

    【讨论】:

      猜你喜欢
      • 2012-11-14
      • 2018-01-16
      • 2014-09-06
      • 2021-02-21
      • 2019-11-27
      • 2021-02-12
      • 2011-11-20
      • 2018-10-26
      • 2019-07-27
      相关资源
      最近更新 更多