Reputation: 31109
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
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
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
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
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