Объектно-ориентированное программирование
Наследование — одна из концепций объектно-ориентированного программирования (ООП). Наследование позволяет объявить класс,
Синтаксис
Задача. Пример наследования
Объяснение
Давайте создадим класс Triangle:
В классе есть и собственный метод findArea(). Он вычисляет площадь треугольника и выводит ее на экран. Попробуем запустить нашу
from math import pi
Наблюдение: Square – частный случай Rectangle
Задача: реализовать метод perimeter() у всех фигур
Задача: реализовать метод perimeter() у всех фигур
Наследование
Синтаксис наследования
Иерархия классов
Иерархия геометрических фигур
Механизм наследования
Механизм наследования
Механизм наследования
Механизм наследования
Пример
Наследование на примере геометрических фигур
Использование методов базового класса
Использование методов базового класса
Использование методов базового класса
Как добавить в производный класс новый метод, которого нет в базовом классе.
Квадратура круга
Квадратура круга
Множественное наследование
Множественное наследование — это возможность класса иметь более одного родительского класса. 
Многоуровневое наследование
Мы также можем наследовать класс от уже наследуемого. Это называется многоуровневым наследованием. Оно может иметь сколько
Класс Derived1 наследуется от класса Base, а класс Derived2 — от класса Derived1.
Расширение методов
Иерархия геометрических фигур
Наследуем класс Square от класса Rectangle
Наследуем класс Square от класса Rectangle
Расширение метода
Расширение метода
Использование методов наследников в базовом классе
Задача: вывести на экран «описание» фигуры
Если на вход этой функции подать переменную неправильного типа?
Правильное решение – добавить соответствующий метод в базовый класс
Методы наследников в базовом классе
Переопределение методов
«Починим» метод describe() для класса Shape
Переопределение методов
Множественное наследование
Множественное наследование
Пример
Проблемы множественного наследования
Множественное наследование: пример
Пример
Пример
404.15K
Category: programmingprogramming

ООП: Наследование в Python

1. Объектно-ориентированное программирование

Занятие №16
Объектно-ориентированное
программирование

2. Наследование — одна из концепций объектно-ориентированного программирования (ООП). Наследование позволяет объявить класс,

Наследование — одна из концепций объектноориентированного программирования (ООП).
Наследование позволяет объявить класс, который
дублирует функциональность уже существующего
класса.
С помощью этой концепции вы сможете расширить
возможности своего класса.

3. Синтаксис

class BaseClass:
Тело родительского класса
class DerivedClass(BaseClass):
Тело дочернего класса

4. Задача. Пример наследования

Полигон — замкнутая геометрическая фигура. У полигона 3 и более сторон.
class Polygon:
def __init__(self, no_of_sides):
self.n = no_of_sides
self.sides = [0 for i in range(no_of_sides)]
def inputSides(self):
self.sides = [float(input("Введите сторону " + str(i+1)+ " : ")) for i in range(self.n)]
def dispSides(self):
for i in range(self.n):
print("Сторона", i+1, " — ", self.sides[i])

5. Объяснение

В этом классе объявлено несколько переменных. Одна хранит количество
сторон — n. Вторая, sides — это список, в нём находятся размеры сторон.
Метод inputSides() принимает размер каждой стороны, а dispSides() выводит
их на экран.
Треугольник — это полигон с 3 сторонами. Теперь мы можем создать класс
Triangle, который наследует весь функционал Polygon. Благодаря этому все
атрибуты класса Polygon становятся доступны в Triangle.
Так что нам не нужно объявлять все переменные и методы снова.

6. Давайте создадим класс Triangle:

class Triangle(Polygon):
def __init__(self):
Polygon.__init__(self,3)
def findArea(self):
a, b, c = self.sides
# Вычисляем полупериметр
s = (a + b + c) / 2
area = (s*(s-a)*(s-b)*(s-c)) ** 0.5
print('Площадь треугольника равна %0.2f' %area)

7. В классе есть и собственный метод findArea(). Он вычисляет площадь треугольника и выводит ее на экран. Попробуем запустить нашу

программу:
>>> t = Triangle()
>>> t.inputSides()
Введите сторону 1 : 3
Введите сторону 2 : 5
Введите сторону 3 : 4
>>> t.dispSides()
Сторона 1 — 3.0
Сторона 2 — 5.0
Сторона 3 — 4.0
>>> t.findArea()
Площадь треугольника равна 6.

8. from math import pi

Рассмотрим следующий пример:
from math import pi
class Circle:
def init (self, radius):
self.r = radius
def area(self):
return pi * self.r ** 2
class Rectangle:
def init (self, a, b):
self.a = a
self.b = b
def area(self):
return self.a * self.b

9. Наблюдение: Square – частный случай Rectangle

class Square:
def
init
=a
(self, a): self.a
def area(self):
return self.a * self.a

10. Задача: реализовать метод perimeter() у всех фигур

Сейчас нам нужно добавитьего во все три класса — но это лишняя
работа, ведьпериметр квадрата вычисляется так же, каки периметр
прямоугольника.
А любаялишняя работа не только отнимает время
у программиста, но и увеличивает вероятность допустить ошибку.

11. Задача: реализовать метод perimeter() у всех фигур

Резюмируем наши наблюдения:
– Класс Square является частным случаем класса Rectangle;
– Если бы имелсяспособ явно запрограммироватьэто отношение,
то наш код получился более коротким и,что важно,
согласованным (еслипериметр прямоугольника вычисляется
правильно,то периметр квадрата автоматически вычисляется
правильно).

12. Наследование

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

13. Синтаксис наследования

Чтобы наследовать класс B от класса A (то есть, чтобы
запрограммировать отношение «класс B являетсячастным случаем
класса A»),нужно написать так:
class A:
pass
class B(A):
pass

14. Иерархия классов

15. Иерархия геометрических фигур

Shape
Circle
Rectangle
Square

16. Механизм наследования

17. Механизм наследования

Вопрос:Что происходит, когда класс B наследуется от класса A (т.е.
когда мы пишем class B(A))?
Ответ:Меняется процедура поиска методов и атрибутов в
классе B.

18. Механизм наследования

Мы знаем, что у «простого» объекта (который ни от кого не
наследуется) нельзя вызвать несуществующий метод или
прочитать несуществующий атрибут:
class C:
def foo(self):
print('foo')
c = C()
c.foo()
c.bar()
# ok
# error

19. Механизм наследования

Если класс наследован от другого класса, то проверка
существования метода (или атрибута) осуществляется так:
– сперва метод ищется в исходном (производном) классе;
– если его там нет,он ищется в базовом классе;
– предыдущие шаги повторяются до тех пор, пока метод не будет
найден, или пока процедура не дойдет до класса, который ни от
кого не наследуется.

20. Пример

class BaseC:
def bar(self):
print('bar')
class C(BaseC):
def foo(self):
print('foo')
c = C()
c.foo()
c.bar()
c.baz()
классе
# ok
-- этот метод есть в производном классе
# ok
-- этот метод есть в базовом классе
# error -- этого метода нет ни в производном, ни в базовом

21. Наследование на примере геометрических фигур

22. Использование методов базового класса

Реализуем в классе Shape метод describe, который будет
печатать название собственного класса:
class Shape:
def describe(self):
# Атрибут class
содержит класс или тип объекта self # Атрибут
name
содержит строку,
# в которой написано название класса или типа print("Класс:
{}".format(self. class . name ))

23. Использование методов базового класса

Убедимся, что метод describe работает для классов Circleи Rectangle
from math import pi
class Circle(Shape):
def init (self, radius): self.r = radius
class Rectangle(Shape):
def init (self, a, b): self.a = a
self.b = b
def area(self):
return pi * self.r ** 2
def area(self):
return self.a * self.b

24. Использование методов базового класса

shape = Shape()
shape.describe() # =>
Класс: Shape
circle = Circle(1)
circle.describe() # =>
Класс: Circle
rectangle = Rectangle(1, 2)
rectangle.describe()
# => Класс: Rectangle

25. Как добавить в производный класс новый метод, которого нет в базовом классе.

Добавим в классCircleметод square,который решает знаменитую
задачу квадратуры круга — возвращает квадрат (внашем случае
— объект Rectangle с равными сторонами), который по площади
равен площади исходного круга.

26. Квадратура круга

class Circle(Shape):
def init (self, radius):
self.r = radius
def area(self):
return pi * self.r ** 2
def square(self):
side = pi ** 0.5 * self.r
return Rectangle(side, side)

27. Квадратура круга

circle
= Circle(1)
square = circle.square()
print("Площадь круга: {}".format(circle.area())) # => Площадь круга:
3.141592653589793
print("Площадь квадрата: {}".format(square.area())) # => Площадь
квадрата: 3.1415926535897927
print("Радиус круга: {}".format(circle.r)) # => Радиус круга: 1
print("Длина стороны квадрата: {}".format(square.a)) # => Длина стороны
квадрата: 1.7724538509055159

28. Множественное наследование

29. Множественное наследование — это возможность класса иметь более одного родительского класса. 

Множественное наследование — это возможность класса
иметь более одного родительского класса.
class Base1:
pass
class Base2:
pass
# этот класс наследует сразу от двух родительских классов:
Base1 и Base2
class MultiDerived(Base1, Base2):
pass

30. Многоуровневое наследование

31. Мы также можем наследовать класс от уже наследуемого. Это называется многоуровневым наследованием. Оно может иметь сколько

угодно уровней.
В многоуровневом наследовании свойства родительского
класса и наследуемого от него класса передаются новому
наследуемому классу.

32. Класс Derived1 наследуется от класса Base, а класс Derived2 — от класса Derived1.

class Base:
pass
class Derived1(Base):
pass
class Derived2(Derived1):
pass

33. Расширение методов

34. Иерархия геометрических фигур

Shape
Circle
Rectangle
Square

35.

from math import pi
class Shape:
def describe(self):
print("Класс: {}".format(self. class . name
class Circle(Shape):
def init (self, radius): self.r = radius
def area(self):
return pi * self.r ** 2
def perimeter(self): return 2 * pi * self.r
class Rectangle(Shape):
def init (self, a, b): self.a = a
self.b = b
def area(self):
return self.a * self.b
def perimeter(self):
return 2 * (self.a + self.b)
))

36. Наследуем класс Square от класса Rectangle

class Square(Rectangle): pass
side = 5
sq = Square(side, side)
print(sq.area())
# => 25
print(sq.perimeter()) # => 20

37. Наследуем класс Square от класса Rectangle

Поскольку мы никак не «заполнили» код класса Square, то он будет
иметь те же самые методы,что были у класса Rectangle.
Мы хотим,чтобыконструктор класса Square принимал на вход
один аргумент (длинустороны).
Однако конструктор класса Rectangle принимает на вход два аргумента
(ширину и высоту). Как быть?

38. Расширение метода

Такаяпроцедура (когда метод производного класса
дополняет аналогичный метод базового класса)
называется расширением метода,а в коде это выглядит
следующим образом:
class Square(Rectangle):
def
init
(self, size): print('Создаём
квадрат') super().
init (size,
size)
Вызываем конструктор базового класса

39. Расширение метода

sq = Square(2)
# => Создаём квадрат
print(sq.area()) # => 4
print(sq.perimeter()) # => 8
print(sq.a)
# => 2

40. Использование методов наследников в базовом классе

Использование методов наследников
вбазовом классе

41. Задача: вывести на экран «описание» фигуры

Напишем универсальную (т.е. полиморфную) функцию для этого:
def describe_shape(shape):
print("Периметр: {}\nПлощадь: {}".format( shape.perimeter(),
shape.area()))
describe_shape(sq)

42. Если на вход этой функции подать переменную неправильного типа?

Программа завершится с ошибкой:
describe_shape(5)
AttributeError
Traceback (most recent call last)
...
AttributeError: 'int' object
has
no attribute 'perimeter'

43. Правильное решение – добавить соответствующий метод в базовый класс

В нашем случае можно просто немного дополнить метод describe
класса Shape:
class Shape:
def describe(self):
print("Класс: {}\nПериметр:
Добавим ещё и название
self. class . name ,
self.perimeter(), self.area()))
{}\nПлощадь:
класса
{}".format( #

44. Методы наследников в базовом классе

Обратите внимание, что у класса Shape нет методов perimeter()и
area(),поэтому метод describe() не будет работать для объектов этого
класса. Но у всех производных классов эти методы есть, поэтому
для них все сработает правильно:
sq = Square(3)
# => Создаём квадрат
sq.describe()
# => Класс: Square
# => Периметр: 12
# => Площадь: 9

45. Переопределение методов

46. «Починим» метод describe() для класса Shape

class Shape:
def describe(self):
print("Класс: {}\nПериметр:
self. class .
self.area()))
def area(self): return
None
def perimeter(self): return
None
name ,
{}\nПлощадь:
{}".format(
self.perimeter(),

47.

shape = Shape()
circle = Circle(5)
rectangle = Rectangle(3, 4)
square = Square(5)
# => Создаём квадрат
shape.describe()
# => Класс: Shape
# => Периметр: None
# => Площадь: None
rectangle.describe()
# => Класс: Rectangle
# => Периметр: 14
# => Площадь: 12
circle.describe()
# => Класс: Circle
# => Периметр: 31.41592653589793
# => Площадь: 78.53981633974483
square.describe()
# => Класс: Square
# => Периметр: 20
# => Площадь: 25

48. Переопределение методов

Это называется переопределением методов.
В отличие от расширения методов,в данном случаеметод area()
базового класса не используется при реализации метода area()
производного класса;то же самое относится и к методу perimeter().

49. Множественное наследование

50. Множественное наследование

Python предоставляет возможность наследоваться сразу от
нескольких классов.
Такой механизм называется множественное наследование, и он
позволяет вызывать в производном классе методы разных
базовыхклассов.

51. Пример

class Base1:
def tic(self):
print("tic")
class Base2:
def tac(self):
print("tac")
class Derived(Base1, Base2): pass
d = Derived()
d.tic()
# метод,
наследованный
от
Base1
d.tac()
# метод,
наследованный
от
Base2

52. Проблемы множественного наследования

– Что, если названиякаких-то методов в базовых классах
совпадают?
– И какой из нихбудет вызван из производного класса?

53. Множественное наследование: пример

Мы знаем,что квадрат является не только прямоугольником, но еще
и правильным многоугольником.
В любой правильный многоугольник, например, можно
вписать окружность, а в произвольный прямоугольник —
нельзя.
Давайте напишем отдельный класс RegularPolygon для
правильных многоугольников.

54. Пример

from math import tan, pi
class RegularPolygon:
def init (self, side,
n): self.side = side
стороны self.n = n
# число
# длина
сторон
def inscribed_circle_radius(self): '''Радиус вписанной
окружности'''
return self.side / (2 * tan(pi / self.n))

55. Пример

Квадрат можно отнаследовать от прямоугольника и правильного
многоугольника. Обратите внимание на конструктор класса Square:
class Square(Rectangle, RegularPolygon): def init (self,
a):
# Приходится явно вызывать конструкторы базовых классов Rectangle. init
(self, a, a)
RegularPolygon. init (self, a, 4)
s = Square(5)
s.describe()
# метод
класса Rectangle
print(s.inscribed_circle_radius())
# метод
класса RegularPolygon
English     Русский Rules