2015-08-06 2 views
3

Я изучаю Python на 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 

ответ

3

Тем не менее, в коде ниже, инициализации повторно инициализирует модель, цвет и миль на галлон. Почему это?

Поскольку автор 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 градусов (или любое фиксированное число). Было бы разумнее пропустить только два угла.

+0

'super (Equilateral, self) .__ init __ (угол, угол, угол)': это не работает (вызывает 'NameError'). Метод автоматически не имеет доступа к пространству имен этого класса. Если вы хотите получить доступ к 'angle', вам нужно использовать' Equilateral.angle' или 'self.angle'. – dhke

+0

@ dhke да, конечно, спасибо. Исправлена ​​ошибка. – juanchopanza

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 работает, либо у него/нее была явная причина не вызывать инициализатор суперкласса.

+0

whyd o вы пишете 'self.angle' в' Equilateral', когда juanchopanza пишет 'angle'? – AlanH

+0

@AlanH: его код вызывает 'NameError'. В методах python автоматически не имеют доступа к переменным класса и экземпляра. Вам нужно явно ссылаться на них либо с помощью 'Equilateral.angle', либо' self.angle'. 'self.angle' скрывает тот факт, что это переменная класса, но избегает дублирования имени класса. – dhke

+0

@AlanH В моем коде была ошибка. См. Редактирование. Обратите внимание, что в этом случае я предпочитаю использовать имя класса для 'self', потому что он дает понять, что это переменная класса. Но это личный выбор. 'self' отлично работает. – juanchopanza

1

В Python классы работают как лук. Самый внешний слой - это объект «экземпляр», self. Слой ниже - это объект класса, сам по себе является экземпляром. Слои ниже это объекты класса, которые унаследованы от (базовых классов).

Ввод чего-то в определение класса, как number_of_sides в Triangle, помещает что-то в объект класса. Присвоение self помещает что-то в объект экземпляра, другой слой лука.

При разрешении имени типа self.angle, Python начинает смотреть на слой self. Если он не найден там, он смотрит на слой ниже и так далее. В примере Equilatoral angle не найден в self, но он находится в классе Equilatoral. angle1 известен только в переменной экземпляра self, а не в переменной класса Equilatoral.

В примере автомобиля переменных model, color и mpg сохраняется в экземпляре, а не в самом классе. Они создаются, когда вызывается функция Car.__init__, а не унаследована по некоторому магическому коду. Это по дизайну, поскольку Python предпочитает явно скрытое поведение. Как объясняется juan, вы должны явно вызвать конструктор базового класса для полной инициализации объекта экземпляра.

Сначала необходимость явного вызова конструкторов баз данных кажется обременительной, но на языках, которые предпочитают неявные действия (например, C++ и ее производные), вам нужны всевозможные синтаксические кошмары, чтобы явно переопределить неявное поведение. Это одна из причин, почему Python имеет такую ​​мягкую кривую обучения.

 Смежные вопросы

  • Нет связанных вопросов^_^