Convert a BaseClass object into a SubClass object idiomatically?

时光怂恿深爱的人放手 提交于 2019-11-29 18:45:35

问题


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.


回答1:


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



回答2:


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


来源:https://stackoverflow.com/questions/18020074/convert-a-baseclass-object-into-a-subclass-object-idiomatically

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!