关于python:以惯用方式将BaseClass对象转换为SubClass对象?

Convert a BaseClass object into a SubClass object idiomatically?

有一个基类Base和一个子类Special

1
2
3
4
5
6
7
8
9
10
11
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__

1
2
3
4
5
6
7
class Special(Base):
    ...
    @classmethod
    def from_base(cls, baseobj):
        special = Special()
        special.__dict__ = baseobj.__dict__
        return special

这是习语吗?如果不是,会是什么?

P.S.一个示例场景:基类是一些默认实现。在野外,您可能会找到基类的对象。现在,在一些项目中,基类是子类,并向子类添加了特殊的方法。现在您仍然主要使用基类对象,但有时您会希望"升级"到特殊类,因为您需要访问某些方法。


您可以通过定义一个备用构造函数并重新分配实例的__class__属性来实现这一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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,它将更改传递给它的任何对象的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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'>


实际上你可以,但我认为你不应该。而不是打字,python有duck-typing来解决这种情况。

不管怎样,代码如下:

1
2
3
4
5
6
7
8
>>> 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?"