There is a base class Base
and a subclass Special
.
class Base(object):
def __init__(self, name):
self.name = name
def greet(self):
return 'Hello %s' % self.name
class Special(Base):
def __init__(self, name):
super(Special, self).__init__(name)
def rhyme(self):
return 'Hi %s! How are you? Fine, thanks. What about you?' % self.name
How can I turn an instance of Base
into an instance Special
? Currently I have a classmethod
defined on Special
that just reassignes __dict__
:
class Special(Base):
...
@classmethod
def from_base(cls, baseobj):
special = Special()
special.__dict__ = baseobj.__dict__
return special
Is this idiomatic? If not what would be?
P.S. An example scenario: The base class is some default implementation. In the wild, you'll likely find objects of the base class. Now in some project, the base class has been subclasses and special methods have been added to the subclass. Now you mostly still work with base class objects, but from time to time you'll want to "upgrade" to the special class, because you'll need access to some methods.
You can achieve this by defining an alternate constructor and reassigning the instance's __class__
attribute.
class Base(object):
def __init__(self, name):
self.name = name
def greet(self):
return 'Hello %s' % self.name
@classmethod
def alt_constructor(cls, *args, **kwargs):
obj = cls(*args, **kwargs)
obj.__class__ = Special
return obj
class Special(Base):
def __init__(self, name):
super(Special, self).__init__(name)
def rhyme(self):
return 'Hi %s! How are you? Fine, thanks. What about you?' % self.name
>>> s = Base.alt_constructor("test")
>>> print s.rhyme()
Hi test! How are you? Fine, thanks. What about you?
EDIT:
Moved the constructor from Special
to Base
.
If you can't modify the Base
class you can add a classmethod to Special
that will change the class of any object passed to it.
class Base(object):
def __init__(self, name):
self.name = name
def greet(self):
return 'Hello %s' % self.name
class Special(Base):
def __init__(self, name):
super(Special, self).__init__(name)
def rhyme(self):
return 'Hi %s! How are you? Fine, thanks. What about you?' % self.name
@classmethod
def convert_to_special(cls, obj):
obj.__class__ = Special
>>> b = Base("test")
>>> print type(b)
<class '__main__.Base'>
>>> Special.convert_to_special(b)
>>> print type(b)
<class '__main__.Special'>
A more all purpose solution would be to create a mixin that can be added to any class.
class ConverterMixin(object):
@classmethod
def convert_to_class(cls, obj):
obj.__class__ = cls
class Special(ConverterMixin, Base):
def __init__(self, name):
super(Special, self).__init__(name)
def rhyme(self):
return 'Hi %s! How are you? Fine, thanks. What about you?' % self.name
>>> b = Base("test")
>>> print type(b)
<class '__main__.Base'>
>>> Special.convert_to_class(b)
>>> print type(b)
<class '__main__.Special'>
actually you can, but I don't think you should. instead of typecasting python has duck-typing to solve this kind of situation.
anyway, here's the code:
>>> base = Base("I'm a base!")
>>> hasattr(base, 'rhyme')
False
>>> base.__class__ = Special
>>> hasattr(base, 'rhyme')
True
>>> base.rhyme()
"Hi I'm a base!! How are you? Fine, thanks. What about you?"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With