有关子类化的问题

时间:2018-12-13 16:51:24

标签: python python-3.x

我整个上午都阅读过有关子类化的知识,但仍然有一些问题。假设下面有Car类:

class Car():
    doors=4
    color='red'
    def __init__(self,gas,miles):
        self.gas=gas
        self.miles=miles
    @property #responsible for handing back the value of the variable gas
    def gas(self):
        return self._gas
    @gas.setter #responsible for setting the value of the variable gas
    def gas(self,x):
        if x<0:
            raise ValueError('Gas cannot be negative')
        self._gas=x

使用门和颜色的默认值实例化该类。我们还使用装饰器检查gas的值是否为负。这一切对我来说都是有意义的。但是,假设我们创建了一个ElectricCar子类:

class ElectricCar(Car):
    def __init__(self,battery_charge,miles):
        self.battery_charge=battery_charge
        super().__init__(miles)

这不起作用。一些问题:

  1. 如果我们使用super().__init__,是否可以从父类继承所有属性?
  2. 在这种情况下,ElectricCar不需要加油值;我们需要从Car继承此属性吗?
  3. 使用super().__init__时是否需要引用父类构造函数的所有属性?对于ElectricCar,我们是否需要编写:super().__init__(gas, miles)

2 个答案:

答案 0 :(得分:4)

要回答您的问题:

通过将CarElectricCar子类化,您创建了一个名为“ ElectricCar”的新类,除了添加到{{1}上的所有内容之外,该类还包含Car类的所有属性,属性和功能。 }。电动车是汽车,还有更多。通过调用ElectricCar,您可以在类的父类{在本例中为super().__init__上调用构造函数(__init__函数)。您会收到错误消息,因为此函数需要两个参数:Cargas,但是您只提供了一个。调用任何方法时,您必须提供该方法的所有必需参数。

在您的情况下,您的miles不需要ElectricCar,因为它代表的是电动汽车。但是,在Python中您没有选择说“我想从gas继承这个,但不是那个”。因此,您的体系结构存在问题,因为您假设汽车的基本情况是汽油车。因此,应该从Car中删除gas属性,并创建一个封装该信息的Car类。

答案 1 :(得分:1)

让我们举一个非常简单的例子:

class Foo:
    ca1="a class attribute in Foo"
    def __init__(self, dv1, dv2="a default value"):
        self.ia="an instance attribute in Foo"
        self.dv1=dv1
        self.dv2=dv2

class Bar(Foo):
    ca2="a class attribute in Bar"
    def __init__(self, dv="new default from Bar"):
        self.ia="an instance attribute in Bar"
        super().__init__("from Bar 1", dv)

Bar的任何实例都将从Foo继承所有 而不是专门添加或覆盖的属性。

为了演示,您可以使用inspect module来显示每个实例的属性。 (我正在过滤内部方法和属性,仅关注用户属性):

>>> import inspect
>>> [a for a in inspect.getmembers(Foo("positional argument"), lambda at: not(inspect.isroutine(at))) if not(a[0].startswith('__'))]
[('ca1', 'a class attribute in Foo'), 
 ('dv1', 'positional argument'), 
 ('dv2', 'a default value'), 
 ('ia', 'an instance attribute in Foo')]  # I added the new lines...

您可以在Foo的实例中看到类和实例变量。

现在来看一个Bar的实例:

>>> [a for a in inspect.getmembers(Bar(), lambda at: not(inspect.isroutine(at))) if not(a[0].startswith('__'))]
[('ca1', 'a class attribute in Foo'), 
 ('ca2', 'a class attribute in Bar'), 
 ('dv1', 'from Bar 1'), 
 ('dv2', 'new default from Bar'), 
 ('ia', 'an instance attribute in Foo')]

如果从super的{​​{1}}中删除__init__,则永远不会调用Bar中的__init__。结果是可见的,与Foo中的__init__相关的隐式代码(如实例值的分配)未完成:

Foo

区别在于class Bar(Foo): ca2="a class attribute in Bar" def __init__(self, dv="new default from Bar"): self.ia="an instance attribute in Bar" 中的实例属性不会作为属性添加到Foo的实例中(以及对{{1}的实例所做的任何其他特定添加) }):

Bar