Идиоматически преобразовывать объект BaseClass в объект SubClass?

Существует базовый класс Base и подкласс 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

Как я могу превратить экземпляр Base в экземпляр Special? В настоящее время у меня есть classmethod, определенный в Special, который просто переназначает __dict__:

class Special(Base):
    ...
    @classmethod
    def from_base(cls, baseobj):
        special = Special()
        special.__dict__ = baseobj.__dict__
        return special

Является ли это идиоматическим? Если бы не то, что было бы?

P.S. Пример сценария: базовый класс - это реализация по умолчанию. В дикой природе вы, скорее всего, найдете объекты базового класса. Теперь в каком-то проекте базовый класс был подклассом, и к подклассу были добавлены специальные методы. Теперь вы в основном по-прежнему работаете с объектами базового класса, но время от времени вам нужно "обновить" до специального класса, потому что вам понадобится доступ к некоторым методам.

Ответ 1

Вы можете достичь этого, указав альтернативный конструктор и переназначив атрибут __class__.

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:

Переместил конструктор из Special в Base.

Если вы не можете изменить класс Base, вы можете добавить classmethod к 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

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

Больше всего целевого решения было бы создать mixin, который можно добавить в любой класс.

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

Ответ 2

на самом деле вы можете, но я не думаю, что вам нужно. вместо typecasting python имеет утиную типизацию, чтобы решить эту ситуацию.

в любом случае, здесь код:

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