Similar presentations:
ООП: Наследование в 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. Иерархия геометрических фигур
ShapeCircle
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и Rectanglefrom 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. Иерархия геометрических фигур
ShapeCircle
Rectangle
Square
35.
from math import piclass 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): passside = 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, piclass 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
programming