装饰类继承和类方法

时间:2012-11-26 22:38:29

标签: python decorator subclassing class-method

如何以正确的方式装饰一个类,以便能够从装饰的类中继承一个类?是否存在正确的方法?

如果我会这样做:

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的同样问题,函数没有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__修改类实例,但这绝对是另一个故事......

4 个答案:

答案 0 :(得分: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__()方法执行单例魔术。这个类是可子类化的。

然而,几乎没有任何理由创建单身人士。通常它只是隐藏全局状态的一种方式,可以更好地消除。

答案 1 :(得分:0)

您的装饰者需要返回一个类才能将其子类化。

>>> 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
>>> 

答案 2 :(得分:0)

很少考虑这个结果:

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的回答和评论的启发,我来到了另一个解决方案。 顺便说一句,单身只是一个例子,它与这个问题没有关系。

答案 3 :(得分:0)

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

有关完整示例,请参阅

Inheriting from decorated classes