【问题标题】:Decorated classes inheritance and classmethods装饰类继承和类方法
【发布时间】:2012-11-14 11:48:45
【问题描述】:

如何以正确的方式装饰一个类,以便能够从被装饰的类中继承一个类?有没有正确的方法?

如果我会这样做:

def singleton(cls):                            
    inst = {}                                  
    def get(*args, **kwargs):                  
        cls_id = id(cls)                       
        if cls_id not in inst:                 
            inst[cls_id] = cls(*args, **kwargs)
        return inst[cls_id]                    
    return get 


@singleton
class A(object):
    @classmethod
    def cls_meth(cls):
        return cls                             

我没有机会从上面继承一个类,因为在我调用它之前这是函数。 classmethods同样的问题,function没有classmethods。

class B(A): # No way! `A` is the function!
    pass

A.cls_meth() # AttributeError: 'function' object has no attribute 'cls_meth'    

即使我在做类似的事情:

class SingletonDecorator(object):                        
    inst = {}                                            
    def __init__(self, cls):                             
        self.cls = cls                                   

    def __call__(self, *args, **kwargs):                 
        cls_id = id(self.cls)                            
        if cls_id not in self.inst:                      
            self.inst[cls_id] = self.cls(*args, **kwargs)
        return self.inst[cls_id]                         


@SingletonDecorator
class A(object):
    pass

当我从A 类继承一个类时,它将从SingletonDecorator 类而不是A 继承。

class B(A): # No no no! I do not need a `SingletonDecorator` child class...
    pass

有一种方法可以通过__metaclass__ 修改类实例,但这绝对是另一回事...

【问题讨论】:

  • 这与你的问题无关,但不需要使用类的id()作为dict键,你可以使用类本身(类是可散列的)。跨度>

标签: python decorator subclassing class-method


【解决方案1】:

您的装饰器需要返回原始类(或其子类)才能进一步对其进行子类化。您的装饰器在用作装饰器时会返回其自身的一个实例(因为这是调用类的默认行为)。

例如,像这样的:

def singleton(cls):
    class C(cls):
        _instance = None
        def __new__(c, *args, **kwargs):
            if type(c._instance) != c:
                c._instance = cls.__new__(c, *args, **kwargs)
            return c._instance
    C.__name__ = cls.__name__
    return C

这将返回原始类的子类,该子类具有重写的 __new__() 方法,该方法可以实现单例魔术。这个类是可子类化的。

然而,几乎没有任何理由要创建单例。通常它只是一种隐藏全局状态的方法,最好消除它。

【讨论】:

    【解决方案2】:

    您的装饰器需要返回一个类以便您对其进行子类化。

    >>> def decorator(cls):
    ...     class Stuff(cls):
    ...         @classmethod
    ...         def print_result(kls):
    ...             print 'results= %s' % kls.results
    ...     return Stuff
    ... 
    >>> class Foo(object):
    ...     results = 'grade A'
    ...     @classmethod
    ...     def print_result(cls):
    ...         print cls.results
    ... 
    >>> 
    >>> @decorator
    ... class Bar(Foo):
    ...     pass
    ... 
    >>> class Baz(Bar):
    ...     def frobnicate(self):
    ...         pass
    ... 
    >>> 
    >>> Baz.print_result
    <bound method type.print_result of <class '__main__.Baz'>>
    >>> Baz.print_result()
    results= grade A
    >>> Foo.print_result
    <bound method type.print_result of <class '__main__.Foo'>>
    >>> Foo.print_result()
    grade A
    >>> 
    

    【讨论】:

      【解决方案3】:

      对这个结果稍加思考:

      class SD(object):                                          
          """ The right way to decorate classes """              
          inst = {}                                              
          def __init__(self, cls):                               
              self.__dict__.update(cls.__dict__)                 
              self.__name__ = cls.__name__                       
              self.cls = cls                                     
      
          def __call__(self, *args, **kwargs):                   
              if self.cls not in self.inst:                      
                  self.inst[self.cls] = self.cls(*args, **kwargs)
              return self.inst[self.cls]                         
      

      受@kindall 的回答和评论的启发,我找到了另一个解决方案。 顺便说一下,单例只是一个例子,它与问题没有真正的关系。

      【讨论】:

      • 但是用这个@SD 装饰器装饰的类不能继承自:TypeError: __init__() takes 2 positional arguments but 4 were given
      【解决方案4】:

      为了能够从装饰类继承,您必须确保装饰器返回装饰类本身而不是包装器。您可以通过重载__new__()-operator 来操作从此类创建新对象。通过这样做,如果装饰器本身是一个类,您甚至可以调用装饰器方法。当然你也可以对装饰器使用继承。

      完整示例见

      Inheriting from decorated classes

      【讨论】:

        猜你喜欢
        • 2018-01-16
        • 2019-11-27
        • 2011-03-23
        • 2011-11-20
        • 2018-07-10
        • 2017-07-05
        • 2014-09-06
        • 2021-02-21
        • 2023-04-09
        相关资源
        最近更新 更多