在Python中从子类访问父类的装饰器

12 投票
2 回答
5333 浏览
提问于 2025-04-16 02:24

怎么在子类中访问父类的装饰器呢?

我之前以为这样做就可以了:

class baseclass(object):
    def __init__(self):
        print 'hey this is the base'

    def _deco(func):
        def wrapper(*arg):
            res = func(*arg)
            print 'I\'m a decorator. This is fabulous, but that colour, so last season sweetiedarling'
            return res
        return wrapper

    @_deco
    def basefunc(self):
        print 'I\'m a base function'

这个类运行得很好,但当我创建一个继承自这个类的子类时:

class otherclass(baseclass):
    def __init__(self):
        super(otherclass, self).__init__()
        print 'other class'


    @_deco
    def meh(self):
        print 'I\'m a function'

这个子类根本无法正常导入,更别提运行了。@_deco是未定义的。尝试使用baseclass._deco会出现一个未绑定的方法 _deco() 错误,这其实也不意外。

有没有什么办法可以做到这一点?我真的很想把装饰器封装在类里面,但我并不执着于这个想法,我需要在父类和子类中都能调用它。

2 个回答

3

在子类中使用这个装饰器而不提到父类,确实有一种专门针对Python3的方法,正如原问题所提到的。这需要在父类的元类中实现这个装饰器(关于元类的详细解释可以在这里找到),并使用它的__prepare__()方法。

aaronasterling的回答是解决这个问题的有效且推荐的方法,我发布这个内容只是作为一个有趣的例子,帮助其他人理解语言的基础知识。只有在没有其他方法可以实现你需要的功能时,才使用元类!

class metaclass(type):
    @classmethod
    def __prepare__(metacls, name, bases):
        def _deco(func):
            def wrapper(*arg):
                res = func(*arg)
                print('I\'m a decorator. This is fabulous, but that colour, so last season sweetiedarling')
                return res
            return wrapper
        return {"_deco": _deco}

class baseclass(metaclass=metaclass):
    def __init__(self):
        print('hey this is the base')

    @_deco
    def basefunc(self):
        print('I\'m a base function')
        
class otherclass(baseclass):
    def __init__(self):
        super(otherclass, self).__init__()
        print('other class')

    @_deco
    def meh(self):
        print('I\'m a function')

下面的示例代码在Python3中运行良好:

>>> obj = otherclass()
hey this is the base
other class
>>> obj.meh()
I'm a function
I'm a decorator. This is fabulous, but that colour, so last season sweetiedarling

关于__prepare__()方法的重要说明:

  • 如果存在,它会在对象主体执行之前运行
  • 它的返回值会作为类主体评估开始时的局部命名空间(这样,装饰器可以在子类的主体中使用,而不需要使用父类的命名空间)
  • 它应该被实现为classmethod(),并且应该返回一个映射对象(例如dict
  • 如果没有这个方法,则会使用空映射作为初始局部命名空间。
15

在编程中,有时候我们需要从一个地方获取数据,然后把这些数据放到另一个地方。这个过程就像是把水从一个杯子倒到另一个杯子一样简单。

有些时候,我们会遇到一些问题,比如数据的格式不对,或者我们想要的数据不在我们想象的地方。这就像是你想喝水,但杯子里却是果汁,这时候你就需要先把果汁倒掉,再装水。

为了避免这些麻烦,我们可以使用一些工具和方法来帮助我们更顺利地获取和处理数据。就像在厨房里使用不同的器具来做饭一样,选择合适的工具可以让我们的工作变得更轻松。

总之,处理数据的过程可能会遇到各种各样的问题,但只要我们掌握了一些基本的方法和技巧,就能更好地应对这些挑战。

class baseclass(object):
    def __init__(self):
        print 'hey this is the base'

    def _deco(func):
        def wrapper(*arg):
            res = func(*arg)
            print 'I\'m a decorator. This is fabulous, but that colour, so last season sweetiedarling'
            return res
        return wrapper

    @_deco
    def basefunc(self):
        print 'I\'m a base function'

    @_deco
    def basefunc2(self):
        print "I'm another base function"

   #no more uses of _deco in this class
    _deco = staticmethod(_deco) 
   # this is the key. it must be executed after all of the uses of _deco in 
   # the base class. this way _deco is some sort weird internal function that 
   # can be called from within the class namespace while said namespace is being 
   # created and a proper static method for subclasses or external callers.


class otherclass(baseclass):
    def __init__(self):
        super(otherclass, self).__init__()
        print 'other class'


    @baseclass._deco
    def meh(self):
        print 'I\'m a function'

撰写回答