I159
I159

Reputation: 31109

Decorated classes inheritance and classmethods

How to decorate a class in the right way to be able to inherit a classes from the decorated one? Is there exists a right way?

If I will do something like:

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                             

I have no chance to inherit a class from above one, because before I call it this is the function. The same problem with classmethods, function have no classmethods.

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

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

Even I doing something like:

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

When I inherit a class from A class, it will be inherited from SingletonDecorator class instead of A.

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

There is a way to modify class instance via __metaclass__, but but that is absolutely another story...

Upvotes: 2

Views: 537

Answers (4)

Florian Reiser
Florian Reiser

Reputation: 131

To be able to inherit from a decorated class, you must ensure, that the decorator returns the decorated class itself and not a wrapper. You can manipulate the creation of new objects from this class by overloading the __new__()-operator. By doing this, you are even able to call decorator methods, if the decorator itself is a class. And of course you can use inheritance for the decorator as well.

For a complete example see

Inheriting from decorated classes

Upvotes: 0

kindall
kindall

Reputation: 184171

Your decorator needs to return the original class (or a subclass of it) to be able to subclass it further. Your decorator returns an instance of itself when used as a decorator (because that's the default behavior of calling a class).

For example, something like this:

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

This returns a subclass of the original class with an overridden __new__() method that does the singleton magic. This class is subclassable.

There is almost never any reason to create a singleton, however. Often it's just a way of hiding global state that would be better eliminated.

Upvotes: 1

I159
I159

Reputation: 31109

little thought given to this result:

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]                         

Inspired of @kindall 's answer and comment, I came to the another solution. By the way, the singleton is just an example, it has no real bearing on the question.

Upvotes: 0

dnozay
dnozay

Reputation: 24314

your decorator needs to return a class in order for you to subclass it.

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

Upvotes: 0

Related Questions