Наследование в ООП на Python. Урок 4

Методическая разработка урока
Элективный курс: Введение в объектно-ориентированное программирование на Python
Уровень: Программирование для начинающих

Одной из важнейших особенностей ООП является возможность наследования объектами атрибутов классов, а также наследование одними классами атрибутов других классов. На самом деле с наследованием мы уже сталкивались, когда создавали любой объект в Python: объекты наследуют атрибуты класса, хотя могут иметь и индивидуальные.

class Things:
     def __init__(self,n,t):
          self.namething = n
          self.total = t
 
th1 = Things("table", 5)
th2 = Things("computer", 7)
 
print (th1.namething,th1.total) # Вывод: table 5
print (th2.namething,th2.total) # Вывод: computer 7
 
th1.color = "green" # новое свойство объекта th1
 
print (th1.color) # Вывод: green
print (th2.color) # ОШИБКА: у объекта th2 нет свойства color!

Здесь оба объекта имеют свойства namething и total, однако только у первого объекта есть свойство color. Все просто: атрибуты класса наследуются объектами, созданными на его основе; однако атрибуты конкретного объекта не зависят от атрибутов других объектов и представляют собственное пространство имен объекта. Последнее позволяет объектам одного класса иметь различные значения атрибутов, а если потребуется и различный набор атрибутов.

Задание. Спишите код, выполните его с помощью интерпретатора Python. Как можно исправить код, чтобы не было ошибки? Исправьте.

На самом деле, наследование более широкое понятие, чем просто взаимосвязь между классами и объектами. Один класс может быть подклассом другого, дополняя его. Пояснить это можно проведя аналогию с реальным миром. Например, все столы имеют общие характерные черты («класс»), при этом они имеют разное назначение («подклассы»), хотя продолжают наследовать общие черты. В результате того, что есть такой механизм как наследование можно избежать избыточность кода, просто описав общие свойства и методы в надклассах.

По поводу терминологии. Классы, атрибуты которых наследуются другими классами, могут называть как надклассами так и суперклассами. Классы, которые наследуют атрибуты других классов, часто называют подклассами.

Класс, являющийся надклассом по отношению к одному классу, сам может быть подклассом по отношению к другому. Другими словами, может существовать целая цепочка наследования.

При обращении к атрибуту объекта (obj.prop) сначала просматривается на наличие этого атрибута сам объект, затем его класс, на основе которого он создан. Если в классе не будет найден атрибут, то его поиск продолжится в суперклассе, к которому относится класс.

Суперклассы класса указываются в скобках в заголовке инструкции class.

Рассмотрим такой пример:

class Table:
     def __init__(self,l,w,h):
          self.long = l
          self.width = w
          self.height = h
     def outing(self):
          print (self.long,self.width,self.height)
 
class Kitchen(Table):
     def howplaces(self,n):
          if n < 2:
               print ("It is not kitchen table")
          else:
               self.places = n
     def outplases(self):
          print (self.places)
 
t_room1 = Kitchen(2,1,0.5)
t_room1.outing()
t_room1.howplaces(5)
t_room1.outplases()
 
t_2 = Table(1,3,0.7)
t_2.outing()
t_2.howplaces(8) # ОШИБКА

Здесь создается два класса: Table и Kitchen. Второй является подклассом первого и наследует все его атрибуты (методы __init__ и outing). Далее создаются два объекта: t_room1 и t_2. Первый объект принадлежит к классу Kitchen и наследует атрибуты этого класса и его суперкласса. Второй объект принадлежит классу Table; к классу Kitchen он никакого отношения не имеет и поэтому не может обращаться к методам howplaces и outplases. В данном примере также можно увидеть, что объекты можно создавать как на основе классов так и суперклассов.

Задание. Расширьте программу, представленную выше, создав второй подкласс класса Table (например, Worker), содержащий пару методов, отличающихся от методов класса Kitchen().

Класс может иметь не один, а несколько суперклассов, которые перечисляются друг за другом в скобках в строке заголовка. Такое наследование называется множественным. Потребность во множественном наследовании возникает в случае, если объекты класса предполагают использование свойств и методов различных суперклассов.

Практическая работа

Напишите программу, где класс «геометрические фигуры» (figure) содержит свойство color с изначальным значением white и метод для изменения цвета фигуры, а его подклассы «овал» (oval) и «квадрат» (square) содержат методы __init__ для задания начальных размеров объектов при их создании.

Что-то здесь не так def

Что-то здесь не так

     def outing(self):
          print ("Long:",self.longe,"Width:",self.width,"Height:",self.height)
 
class Kitchen(Table):
     def howplaces(self,n):
          if n < 2:
               print ("It is not kitchen table")
          else:
               self.places = n
     def outplaces(self):
          print ("Places:",self.places)
class Worker(Table):
     def work_table(self,b):      
         if b == "Yes":
             self.work_table = b
         else:
             print("It is not worker table")
     def outwork(self):
         print (self.work_table)
 
obj = Kitchen(input("Long:"),input("Width:"),input("Height:"))
obj.outing()
obj.howplaces(int(input("How places? ")))
obj.outplaces()
 
obj2 = Table(input("Long:"),input("Width:"),input("Height:"))
obj2.outing()
 
obj3 = Worker(input("Long:"),input("Width:"),input("Height:"))
obj3.work_table(input("Here is possible to put the computer?(Yes/No) "))
obj3.outwork()

полностью рабочий код по

полностью рабочий код по заданию + пара строчек

class Figura:
     def __init__(self):
          self.color='White'
     def colors(self,cl):
          self.color=cl
 
class Oval(Figura):
     def __init__(self):
          Figura.__init__(self)
          self.radius=55
          self.tl=2
          self.form='oval'
 
class Kvadrat(Figura):
     def __init__(self):
          Figura.__init__(self)
          self.a=45
          self.b=33
          self.form='квадрат'
 
ob1=Figura()
print(ob1.color)
ob1.colors("Red")
print(ob1.color)
 
ob2=Oval()
ob2.colors("Yelloy")
print(ob2.radius,ob2.tl,ob2.form,ob2.color)
 
ob3=Kvadrat()
ob3.colors("green")
print(ob3.a,ob3.b,ob3.form,ob3.color)

Не пойму, почему подкласс

Не пойму, почему подкласс Oval не наследует атрибут суперкласса 'color':

class Figure:
     def __init__(self):
          self.color = 'white'
     def changecolor(self, newcolor):
          self.color = newcolor
 
class Oval(Figure):
     def __init__(self):
          self.form = 'oval'
 
obj1 = Figure()
obj2 = Oval()
print obj1.color
print obj2.color #ERROR

Но если добавить:

obj2.changecolor('Black')
print obj2.color #SUCCESS

то скрипт отрабатывает нормально.

Потому что __init__ подкласса

Потому что __init__ подкласса переопределяет __init__ суперкласса. Если бы не было данного метода в Oval, то свойство color наследовалось бы из суперкласса. Следует в конструкторе подкласса вызывать конструктор суперкласса, а потом "дополнять" его кодом специфичным для подкласса:

class Oval(Figure):
	def __init__(self):
		Figure.__init__(self)
		self.form = 'oval'

Это не правильно, вы

Это не правильно, вы пересоздаете экземпляр класса по новой.

По первому заданию, по

По первому заданию, по дополнению, подкласс содержит функцию вычисления
площади объектов из первых двух классов Table и Kitchen
ну и своих тоже (вместо tables подставляем имя объекта, площадь которого нужно посчитать),
и сортирует, если стол большой по площади, это в отдел директорских столов
если меньше, то обычный стол :

class Area(Table):
	def ploshad(self, tables):
		self.area_t = tables.long * tables.width
		if self.area_t>2:
			print (self.area_t, 'stol directora')
		else:
			print (self.area_t, 'work stol')
 
 
>>> t5 = Area(2, 4, 0.7)
>>> t5.ploshad(t1)
1.0 work stol
>>> t5.ploshad(t5)
8.0 stol directora

Если мы сделаем так:
class Area(Table):
	def __init__(self, tables):
		self.area_t = tables.long * tables.width
		if self.area_t>2:
			print (self.area_t, 'stol directora')
		else:
			print (self.area_t, 'work stol')

то при создании объекта класса Area, сначала должны будем обязательно ввести
имя объекта, для которого посчитаем площадь, а уже потом заводить длину,
ширину и высоту
>>> t6 = Area(t5)
8.0 stol directora

Это чтобы самому запомнить)), что все, что в __init__ обязательно для выполнения.

Пример интересный с точки

Пример интересный с точки зрения демонстрации наследования. Все-таки по смыслу Area больше походит на свойство, чем объект.

В ООП программу можно представить как систему взаимодействующих сущностей. Площадь - это характеристика объекта, без него она не существует, поэтому лучше метод ploshad разместить в классе Table.

Решение практической работы

class figure:
    color = "white"
    def changecolor(self, newcolor ):
        self.color = newcolor
 
class oval(figure):
    def __init__(self, r):
        self.luc = r
 
class square(figure):
    def __init__(self, a, b):
        self.dlina = a
        self.shirina = b
 
f = figure()
o = oval(15)
 
s = square(25, 4)
s.changecolor("green")
 
print(o.color, o.luc)
print(s.color, s.dlina, s.shirina)
 
мое прелиминарное решение практической работы :)

А есть смысл создавать обьект

А есть смысл создавать обьект класса фигуры? Мы то используем сам класс, а не обьект..

у квадрата одинаковые стороны

у квадрата одинаковые стороны

Просто изначяльно делал как

Просто изначяльно делал как квадрат, но по ходу переквалифицировал в прямоугольник :)) думаю, что это не столь сушественно.

Хорошая статья, но заметил

Хорошая статья, но заметил один недочет в названии класса:

class Kitchen(Table)

Судя по контексту мы создаем кухонный стол. Но если судить из названия класса - получается казус, так как мы наследуем целую кухню из стола =)

А вложеных классов может быть

А вложеных классов может быть бесконечное колличество)?

и как сделать например 3 класса?
например:

class klass1:
 class klass2(klass1):
  class klass3(???):

а что у трейтьего класса писать? писать class klas3(klass2): или все надклассы перечислить? class klass3(klass1,klass2):

Классы не вкладывают друг в

Классы не вкладывают друг в друга. Если klass1 является родителем для klass2, а klass2 родителем для klass3, то делают так:

...
 
class klass1:
    ...
 
class klass2(klass1):
    ...
 
class klass3(klass2):
    ...
 
...

Возможно, все-таки, вы имели

Возможно, все-таки, вы имели ввиду не def klass1, а class klass1.

Да, спасибо. Исправлено

Да, спасибо. Исправлено

Классы можно вкладывать друг

Классы можно вкладывать друг в друга, но это достаточно редко используется.