将一个基类对象按惯例转换为子类对象?

9

有一个基类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 的实例?目前,我已经在 Special 上定义了一个 classmethod,它只是重新分配了 __dict__

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

这个用法通顺自然吗?如果不是,应该怎么说?

P.S. 一个例子场景:基类是一些默认实现。在实际应用中,你可能会遇到基类的对象。现在在某个项目中,基类已经被子类化,并添加了特殊方法。现在你大多数时间仍然使用基类对象,但是偶尔需要“升级”到特殊类,因为你需要访问一些方法。


为什么无法实例化或强制使用“Special”? - Simeon Visser
2个回答

8
你可以通过定义一个备用构造函数并重新分配实例的 __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?

编辑:

将构造函数从Special移动到Base

如果您无法修改Base类,则可以向Special添加一个classmethod,该方法将更改传递给它的任何对象的类。

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

@miku 你是对的,我的原始答案中我添加了一个替代构造函数到 Base 类中,改变了该类的行为方式。我已经更新了我的答案,包含了我认为你正在寻找的内容。 - FastTurtle
感谢您的更新。使用__class__会快上一个数量级,这并不奇怪,因为它不会创建新对象。 - miku

2
实际上您可以这样做,但我认为您不应该这样做。与其使用类型转换,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?"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接