Python: как ссылаться на переменные-члены
Я изучал Python на Codecademy, и я совершенно сбит с толку. Я не могу понять, как ссылаться на переменные-члены (надеюсь, так они называются). Вот фрагмент кода, который я написал, чтобы продемонстрировать свою путаницу:
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
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)
Также обратите внимание, что нет смысла инициализировать Triangle
с трех сторон, если вы говорите о виде пространства, в котором внутренние углы треугольника составляют до 180 градусов (или любое другое фиксированное число). Было бы разумнее пропустить только два угла.
Обе реализации, кажется, немного не в порядке. В Python суперкласс __init__()
не вызывается автоматически. Вы должны сделать это явно.
Разве он не должен наследоваться так же, как в вышеприведенном коде с подклассом Equilib?
Когда экземпляр 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, либо у него была явная причина не вызывать инициализатор суперкласса.
В Python классы работают немного как лук. Самым внешним слоем является объект 'instance', self
, Слой ниже - это объект класса, экземпляром которого является self. Слои ниже, которые являются объектами класса, от которых унаследованы (базовые классы).
Положить что-то в определение класса, например number_of_sides
в Triangle
, помещает что-то в объект класса. Назначение self
помещает что-то в экземпляр объекта, другой слой лука.
При разрешении имени как self.angle
Python начинает смотреть на self
слой. Если его там нет, он смотрит на слой ниже и так далее. В эквилаторальном примере angle
не найден в self
, но он найден в Equilatoral
учебный класс. angle1
известен только в переменной экземпляра self
, не в переменной класса Equilatoral
,
В примере автомобиля переменные model
, color
а также mpg
хранятся в экземпляре, а не в самом классе. Они создаются, когда функция Car.__init__
называется, не наследуется каким-то магическим кодом. Это сделано специально, так как Python предпочитает явное, а не неявное поведение. Как объяснил juan, вы должны явно вызвать конструктор базового класса, чтобы полностью инициализировать объект экземпляра.
Сначала необходимость явного вызова базовых конструкторов кажется обременительной, но в языках, которые предпочитают неявные действия (такие как C++ и его производные), вам нужны всевозможные синтаксические кошмары для явного переопределения неявного поведения. Это одна из причин, почему у Python такая мягкая кривая обучения.