【问题标题】:Add decorator to a method from inherited class?将装饰器添加到继承类的方法中?
【发布时间】:2018-10-26 05:53:41
【问题描述】:

我想从类继承只是为了给它的方法添加装饰器。

在不重新定义每个基类方法的情况下,有没有捷径可以做到这一点?

【问题讨论】:

  • 你可以通过__getattribute__拦截all属性访问,然后直接在那里应用相同的函数而不是作为装饰器。

标签: python inheritance decorator


【解决方案1】:

可以使用类装饰器来封装整个工作

示例代码:

def deco(func):
    """Function decorator"""
    def inner(*args, **kwargs):
        print("decorated version")
        return func(*args, **kwargs)
    return inner

def decoclass(decorator):
    """Class decorator: decorates public methods with decorator"""
    def outer(cls):
        class inner(cls):
            pass
        for name in dir(cls):
            if not name.startswith("_"):     # ignore hidden and private members
                # print("decorating", name)  # uncomment for tests
                attr = getattr(inner, name)
                setattr(inner, name, decorator(attr))
        return inner
    return outer

class Foo:
    """Sample class""
    def foo(self):
        return "foo in Foo"

然后你就可以使用它了:

>>> @decoclass(deco)
class Foo2(Foo):
    pass

>>> f = Foo2()
>>> f.foo()
decorated version
'foo in Foo'

【讨论】:

    【解决方案2】:

    当然,您可以动态执行此操作。假设你有一些类:

    >>> class Foo:
    ...    def bar(self): print('bar')
    ...    def baz(self): print('baz')
    ...
    

    还有一个装饰器:

    >>> def deco(f):
    ...    def wrapper(self):
    ...       print('decorated')
    ...       return f(self)
    ...    return wrapper
    ...
    

    然后简单地继承:

    >>> class Foo2(Foo):
    ...     pass
    ...
    

    然后循环你原来的类,并将装饰器应用到你的新子类:

    >>> for name, attr in vars(Foo).items():
    ...     if callable(attr):
    ...         setattr(Foo2, name, deco(attr))
    ...
    

    所以...

    >>> x = Foo2()
    >>> x.bar()
    decorated
    bar
    >>> x.baz()
    decorated
    baz
    

    现在,使用if callable(attr) 可能不够严格。您可能想忽略“dunder”方法,因此:

    for name, attr in vars(Foo):
        if callable(attr) and not name.startswith('__'):
            setattr(Foo2, name, attr)
    

    可能更合适。取决于您的用例。

    为了好玩,这里我们也可以使用type构造函数:

    In [17]: class Foo:
        ...:     def bar(self): print('bar')
        ...:     def baz(self): print('baz')
        ...:
    
    In [18]: def deco(f):
        ...:     def wrapper(self):
        ...:         print('decorated')
        ...:         return f(self)
        ...:     return wrapper
        ...:
    
    In [19]: Foo3 = type(
        ...:     'Foo3',
        ...:     (Foo,),
        ...:     {name:deco(attr) for name, attr in vars(Foo).items() if callable(attr)}
        ...: )
    
    In [20]: y = Foo3()
    
    In [21]: y.bar()
    decorated
    bar
    
    In [22]: y.baz()
    decorated
    baz
    

    【讨论】:

    • @absss 按照我上面的顺序。
    • 是他们将 for 循环封装在 Foo2 类中的一种方法,例如在构造函数中 init
    • @absss 没有简单的方法。只是在外面做循环。它有什么不同?
    • 我认为将它放在 Foo2 类中会更干净,更易于维护,因为它与它的定义直接相关
    • @absss 而不是它的正下方?看,这首先不是很干净。我会犹豫自己是否使用这种方法。不过有可能。
    猜你喜欢
    • 2012-11-14
    • 2014-01-20
    • 2021-02-21
    • 2018-01-16
    • 2019-11-27
    • 1970-01-01
    • 2019-10-02
    • 2014-09-06
    • 2019-06-10
    相关资源
    最近更新 更多