继承:将基类实例转换为子类实例

3

我有一个基类的实例,然后我想把它变成这个基类的子类的实例。也许我在解决问题的方式上有误,并且在面向对象编程中有一些重要的东西我没有理解。代码只是为了说明,可以建议非常不同的方法。任何帮助都将不胜感激。

class Car(object):
    def __init__(self, color):
        self.color = color

    def drive(self):
        print "Driving at 50 mph"

class FastCar(Car):
    def __init__(self, color, max_speed=100):
        Car.__init__(self, color)
        self.max_speed = max_speed

    def drive_fast(self):
        print "Driving at %s mph" %self.max_speed

one_car = Car('blue')

# After the instanciation, I discovered that one_car is not just a classic car
# but also a fast one which can drive at 120 mph.
# So I want to make one_car a FastCar instance.

我看到了一个非常相似的问题,但是没有任何答案适用于我的问题:

  • 我不想让FastCar成为一个包装Car的东西,它知道如何快速驾驶:我真的希望FastCar扩展Car;

  • 我不想在FastCar中使用__new__方法对参数进行测试,并决定__new__是否必须返回Car的新实例或我给它的实例(例如:def __new__(cls, color, max_speed=100, baseclassinstance=None))。


你为什么不这么做:one_car = FastCar(one_car.color, 120)?虽然它不是真正的继承或其他东西,但应该能用。 - Bogdan
3
你的面向对象设计似乎有些问题。我想念的是FastCar也会实现drive()方法,但是会以更高的速度执行(就像你已经实现的drive_fast一样)。现在,调用者必须知道类型才能知道调用哪个方法(不好),而不是调用同一个方法,并让各种类型根据需要实现该方法(好)。你也可以通过在FastCar类的末尾添加“drive = drive_fast”来实现这一点。 - PaulMcG
好的。更好的例子:FastCar没有drive_fast方法,但有一个overtake方法,这个方法对于Car是不存在的。 - Andy
@Eugene @agf @Peter 好的,我的示例太简单了。整个问题在于我不想创建一个新对象,也不想有两个不同的实例。如果有帮助的话,假设Car来自外部模块,并且color实际上是Car的私有成员__color。或者Car.__init__ 不仅将 color 分配给 self.color,而且对颜色进行了大量计算(实际上是一个大的 numpy.array),而我不能重复这些计算来创建 FastCar - Andy
由于Python中没有真正的私有成员,因此您可以使用我的两种方法之一的版本--只需将计算属性复制到新实例(方法1),或者在调整其属性时仅更改类和必要的方法(我的方法2)--这都假设您仍然不想拥有条件的__new____init__。但是,如果您认为需要这样做,则可能需要重新设计您的继承/类结构。 - agf
@Andy - 你的“更好的示例”甚至更糟。你的设计是相同的,你只是重命名了问题方法。我使用继承的很多时候都是为了获得多态性,这需要子类到超类的一致调用接口。请在谷歌上搜索“里氏替换原则”。 - PaulMcG
4个回答

3
class FastCar(Car):
    def __init__(self, color, max_speed=100):
        Car.__init__(self, color)
        self.max_speed = max_speed

    def drive_fast(self):
        print "Driving at %s mph" %self.max_speed

    @staticmethod
    def fromOtherCar(car):
        return FastCar(car.color)

actually_fast = FastCar.fromOtherCar(thought_was_classic)

这是标准的方法。

根据实际类布局,您可能可以尝试做一些类似于以下内容的操作:

classic = Car('blue')

classic.__class__ = FastCar
classic.__dict__.update(FastCar(classic.color).__dict__)

classic.drive_fast()

但我不建议这样做——这是一种hack方法,它不总是有效的,而另一种方法更加干净。
编辑:我刚想添加@PaulMcGuire评论中基本上说的内容。遵循他的建议,他是正确的。

0

为什么不只使用一个类?

class Car(object):
    def __init__(self, color, max_speed = 50):
        self.color = color
        self.max_speed = max_speed
    def drive(self):
        print "Driving at %s mph"%self.max_speed

c=Car('blue')
c.max_speed = 100

0
你可以借鉴C++中“拷贝构造函数”的概念来实现这样的功能。
允许Car的构造函数接受一个Car实例,并复制其所有属性。然后FastCar应该接受Car实例或FastCar实例。
因此,要转换汽车,你只需执行one_car = FastCar(one_car)。请注意,这不会影响对原始Car对象的引用,它们仍将指向相同的Car。

0

在面向对象编程中,实例化后更改活动对象的类型(类)并不常见。我只知道两种语言可以允许这样做,但这是一种“肮脏的技巧”。类型(类)的整个目的是事先知道对象可以执行哪些操作和不能执行哪些操作。如果您想要这样的功能,那么您可能误解了面向对象编程的概念。


如果你是 OOP 方面的新手,我认为你可能会想从字符数组中创建一个字符串并改变对象的类型。实际上,他真正想做的是从旧对象的内容创建一个新对象(他只是不知道而已)。 - agf
谢谢,但我不认为这是我想做的。我真的不想创建一个新对象。 - Andy

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