Python:如何引用成员变量

时间:2015-08-06 07:20:22

标签: python class inheritance member

我在Codecademy上学习Python,我很困惑。我无法弄清楚如何引用成员变量(我希望这是他们所谓的)。这是我写的一段代码,用来表明我的困惑:

class Triangle(object):
    number_of_sides = 3

    def __init__(self, angle1, angle2, angle3):
        self.angle1 = angle1
        self.angle2 = angle2
        self.angle3 = angle3

    def check_angles(self):
        return self.angle1 + self.angle2 + self.angle3 == 180

class Equilateral(Triangle):
    angle = 60
    def __init__(self):
        self.angle1 = self.angle
        self.angle2 = self.angle
        self.angle3 = self.angle

因此,在等边子类中,angle1angle2angle3不包含在__init__的参数中。但是,在下面的代码中,__init__会重新初始化modelcolormpg。为什么是这样?它不应该只是像上面代码Equilateral子类一样继承吗?我不明白他们为什么写得不一样。

class Car(object):
    condition = "new"
    def __init__(self, model, color, mpg):
        self.model = model
        self.color = color
        self.mpg   = mpg

    def display_car(self):
        print "This is a %s %s with %s MPG." %(self.color, self.model, str(self.mpg))

    def drive_car(self):
        self.condition = "used"

class ElectricCar(Car):
    def __init__(self, model, color, mpg, battery_type):
        self.model = model
        self.color = color
        self.mpg   = mpg
        self.battery_type = battery_type

3 个答案:

答案 0 :(得分:3)

  

但是,在下面的代码中, init 重新初始化模型,颜色和mpg。这是为什么?

因为ElectricCar的作者希望用户能够使用四个参数初始化ElectricCar

ec = ElectricCar('xyz-500', 'blue', 0.5, 'boxy')

但是,它们应该传递基类“__init__方法的参数:

class ElectricCar(Car):
    def __init__(self, model, color, mpg, battery_type):
        super(ElectricCar, self).__init__(model, color, mpg)
        self.battery_type = battery_type

EquilateralTriangle的情况下,所有角度都相同且必须为60度,因此能够从三个用户提供的角度初始化这样的对象是没有意义的。

关于基类“__init__的相同评论适用:

class Equilateral(Triangle):
     angle = 60
     def __init__(self):
         super(Equilateral, self).__init__(Equilateral.angle,
                                           Equilateral.angle,
                                           Equilateral.angle)

另请注意,如果您正在讨论三角形的内角加起来为180度(或任何固定数字)的空间,则从三个角度初始化Triangle毫无意义。仅传递两个角度会更有意义。

答案 1 :(得分:2)

两种实现似乎都略有偏差。在Python中,超级类“__init__() 会自动调用。你必须明确地这样做。

  

它不应该只是像上面的代码那样继承Equilateral子类吗?

创建Equilateral的实例时,在上述实现中永远不会调用Triangle.__init__()。没有自动初始值设定项继承(这会违反PEP 20“显式优于隐式”)。

Equilateral可能应该更好地阅读:

class Equilateral(Triangle):
     angle = 60
     def __init__(self):
         super(Equilateral, self).__init__(self.angle, self.angle, self.angle)

ElectricCar相同:

class ElectricCar(Car):
    def __init__(self, model, color, mpg, battery_type):
        super(ElectricCar, self).__init__(model, color, mpg)
        self.battery_type = battery_type
  

我不明白为什么他们的写作方式不同。

这个问题很难回答。作者要么没有正确理解Python的继承是如何工作的,要么他/她有明确的理由不调用超类初始化器。

答案 2 :(得分:1)

在Python中,课程有点像洋葱。最外层是“实例”对象self。下面的层是self对象的类对象。下面的层是从(基类)继承的类对象。

在类定义中加入一些东西,比如number_of_sides中的Triangle,会在类对象中添加一些东西。分配给self会在实例对象中放入一些内容,即洋葱的另一层。

在解析self.angle之类的名称时,Python会开始查看self图层。如果在那里找不到它,它会查看下面的图层,依此类推。在Equilatoral示例中,angle中找不到self,但它位于Equilatoral类中。 angle1仅在实例变量self中已知,而不在类变量Equilatoral中。

在Car示例中,变量modelcolormpg存储在实例中,而不是存储在类本身中。它们是在调用函数Car.__init__时创建的,而不是由某些魔术代码继承创建的。这是设计的,因为Python更喜欢显式的隐式行为。正如juan所解释的那样,您必须显式调用基类构造函数来完全初始化实例对象。

首先,显式调用基本构造函数的需要似乎很繁琐,但在喜欢隐式操作的语言(如C ++及其派生类)中,需要各种语法噩梦来显式覆盖隐式行为。这就是为什么Python有这么温和的学习曲线的原因之一。