我在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
因此,在等边子类中,angle1
,angle2
,angle3
不包含在__init__
的参数中。但是,在下面的代码中,__init__
会重新初始化model
,color
和mpg
。为什么是这样?它不应该只是像上面代码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
答案 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示例中,变量model
,color
和mpg
存储在实例中,而不是存储在类本身中。它们是在调用函数Car.__init__
时创建的,而不是由某些魔术代码继承创建的。这是设计的,因为Python更喜欢显式的隐式行为。正如juan所解释的那样,您必须显式调用基类构造函数来完全初始化实例对象。
首先,显式调用基本构造函数的需要似乎很繁琐,但在喜欢隐式操作的语言(如C ++及其派生类)中,需要各种语法噩梦来显式覆盖隐式行为。这就是为什么Python有这么温和的学习曲线的原因之一。