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

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

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

Основные понятия объектно-ориентированного программирования

Класс

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

Пример аналогии из материального мира: Класс можно сравнить с чертежом здания. Он содержит все необходимые инструкции для создания объектов.

Пример реализации на Python:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say_hello(self):
        print(f"Привет, меня зовут {self.name} и мне {self.age} лет.")

Объекты

Объект — это экземпляр класса. Он содержит значения атрибутов, определенных в классе, и может выполнять методы, определенные в классе.

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

Пример2. Класс — автомобиль. Объекты: Lada X-Ray, Mercedes-Benz CLK

Пример реализации на Python:

person = Person("Иван", 30)
person.say_hello() # Вывод: Привет, меня зовут Иван и мне 30 лет.

Атрибуты

Атрибут — это переменная, определенная в классе, которая хранит значение, относящееся к объекту. Атрибуты могут быть публичными или приватными.

Пример аналогии из материального мира: Атрибут можно сравнить с характеристикой объекта, например, цветом или размером. Например, красный автомобиль или количество углов многоугольника

Пример реализации на Python:

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

car = Car("Toyota", "Camry", 2022)

Методы

Метод — это функция, которая определена внутри класса и может быть вызвана у объектов этого класса. Методы в классах описывают действия, которые объекты могут выполнять.

Давай рассмотрим пример с классом «Животное». Представим, что у нас есть класс «Животное», атрибутами которого являются имя животного и его возраст. Методом может быть, например, метод «голос», который описывает звук, который издает животное. Этот метод может иметь доступ к атрибутам класса, таким как имя животного.

Пример кода на Python:

class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def sound(self):
        pass  # этот метод будет определен в дочерних классах


class Cat(Animal):
    def sound(self):
        return "Meow"


class Dog(Animal):
    def sound(self):
        return "Woof"


cat = Cat("Tom", 3)
dog = Dog("Rex", 5)

print(cat.sound())  # выводит "Meow"
print(dog.sound())  # выводит "Woof"

Здесь мы определяем класс «Животное» с методом «звук», который не имеет реализации и будет переопределен в дочерних классах. Затем мы определяем дочерние классы «Кошка» и «Собака», которые наследуют класс «Животное». В каждом дочернем классе мы переопределяем метод «звук», чтобы он возвращал соответствующий звук для каждого класса. Наконец, мы создаем объекты классов «Кошка» и «Собака» и вызываем их методы «звук». В результате мы получаем разные звуки для каждого объекта в зависимости от его класса.

Конструктор и деструктор

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

Конструктор класса обычно используется для инициализации экземпляра класса, установки его начальных значений. В Python, конструктором является метод с именем __init__, который автоматически вызывается при создании экземпляра класса.

Пример конструктора:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

В данном примере, конструктор класса Person инициализирует атрибуты name и age для экземпляра класса при его создании.

Деструктор класса используется для освобождения ресурсов, которые были выделены для экземпляра класса, когда он больше не нужен и удаляется из памяти. В Python, деструктором является метод с именем __del__, который автоматически вызывается при удалении экземпляра класса.

Пример деструктора:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def __del__(self):
        print(f"{self.name} удален из памяти")

person = Person("Alice", 25)
del person # "Alice удален из памяти" будет напечатано в консоль

Ключевое слово self

В примерах программ, в реализации методов, вы могли наблюдать  конструкции вида self

Ключевое слово self используется для обращения к атрибутам и методам класса из его методов. Когда мы создаем экземпляр класса и вызываем его методы, Python автоматически передает этот экземпляр в метод в качестве первого аргумента (названный self по соглашению). Это позволяет методам получать доступ к атрибутам и методам экземпляра класса.

Пример использования self:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def introduce(self):
        print(f"Привет, меня зовут {self.name} и мне {self.age} лет.")
        
person = Person("Alice", 25)
person.introduce() # "Привет, меня зовут Alice и мне 25 лет." будет напечатано в консоль

В данном примере, метод introduce класса Person использует атрибуты name и age, которые принадлежат конкретному экземпляру класса, доступ к которым он получает через self.

Основные принципы ООП

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

Наследование — это один из основных принципов объектно-ориентированного программирования, который позволяет создавать новый класс на основе уже существующего класса, наследуя его свойства и методы.
Для того чтобы создать новый класс, который наследует свойства и методы от уже существующего класса, необходимо указать название базового класса в определении нового класса в качестве аргумента.

Например, если класс A — это базовый класс, а класс B наследует его свойства и методы, то определение класса B будет выглядеть следующим образом:

class A:
    def __init__(self, x):
        self.x = x
    
    def method_a(self):
        print("Метод A")
        
class B(A):
    def __init__(self, x, y):
        super().__init__(x)
        self.y = y
        
    def method_b(self):
        print("Метод B")

В этом примере класс B наследует свойства и методы от класса A, который определяет атрибут x и метод method_a. При этом, класс B определяет свой собственный атрибут y и метод method_b.

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

Ключевое слово super

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

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

Наследование в Python поддерживает множественность, то есть класс может наследовать свойства и методы нескольких базовых классов. В этом случае определение класса-наследника будет выглядеть следующим образом:

class A:
    def method_a(self):
        print("Метод A")
        
class B:
    def method_b(self):
        print("Метод B")
        
class C(A, B):
    pass

Класс C наследует свойства и методы от классов A и B.

Инкапсуляция

Инкапсуляция – это принцип, который заключается в скрытии реализации от пользователя и предоставлении интерфейса для работы с объектом.

Другими словами, вместо того, чтобы напрямую обращаться к свойствам объекта, мы взаимодействуем с ним через его методы. Это позволяет упростить использование объекта и защитить его состояние от некорректного изменения.

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

Пример реализации на Python:

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.__odometer = 0

    def get_odometer(self):
        return self.__odometer

    def update_odometer(self, mileage):
        if mileage >= self.__odometer:
            self.__odometer = mileage
        else:
            print("You can't roll back an odometer!")

my_car = Car('Toyota', 'Corolla', 2020)
my_car.update_odometer(5000)
print(my_car.get_odometer())  # 5000

В примере мы создали класс Car, который имеет атрибуты make, model и year, а также методы get_odometer и update_odometer. Однако, атрибут __odometer был объявлен с двумя подчеркиваниями в начале, что делает его приватным, то есть скрытым от прямого доступа. Вместо этого мы создали методы get_odometer и update_odometer, которые позволяют получать и изменять значение этого атрибута через интерфейс объекта.

Полиморфизм

Полиморфизм — это способность объекта вести себя по-разному в зависимости от контекста использования. Это означает, что объект может быть использован в разных контекстах и при этом вести себя по-разному. В Python полиморфизм реализуется через использование одного и того же метода с различными параметрами.

Примером полиморфизма может служить функция len(), которая возвращает длину объекта в зависимости от типа объекта. Если объект является строкой, то len() возвращает количество символов в строке, если объект — списком, то длина списка и так далее.

Абстракция

Абстракция — это способ представления объектов и их поведения на более высоком уровне абстракции, чем конкретная реализация. Абстракция позволяет скрыть детали реализации и представить объекты в более удобной форме для пользователя. В Python абстракция реализуется через использование классов и интерфейсов.

Примером абстракции может служить класс Shape, который представляет абстрактную геометрическую фигуру. Этот класс может иметь различные методы, например, метод для вычисления площади фигуры или метод для вычисления периметра фигуры. Реализация методов может отличаться для каждого конкретного подкласса Shape, например, для классов Circle, Square и Triangle, но для пользователя эти подклассы выглядят как абстрактные геометрические фигуры со своими особенностями.

Создание собственных операторов

В Python есть возможность создания собственных операторов при реализации классов с помощью перегрузки методов. Перегрузка методов — это способ определения нового поведения для стандартных операторов языка (например, +, -, *, /, <, > и т.д.) для экземпляров классов.

Операторы могут быть перегружены с помощью специальных методов, которые начинаются и заканчиваются двойными подчеркиваниями (__). Например, чтобы определить поведение оператора + для экземпляров класса, можно определить метод add(). Этот метод принимает два аргумента — self (экземпляр класса) и other (другой объект, с которым производится операция сложения), и возвращает результат операции.

Например, рассмотрим класс Vector, который представляет собой вектор в трехмерном пространстве. Мы можем определить операторы сложения и вычитания для векторов следующим образом:

class Vector:
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
        
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
    
    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y, self.z - other.z)

Теперь мы можем складывать и вычитать экземпляры класса Vector, используя стандартные операторы + и -:

v1 = Vector(1, 2, 3)
v2 = Vector(4, 5, 6)

v3 = v1 + v2
v4 = v2 - v1

print(v3.x, v3.y, v3.z) # 5 7 9
print(v4.x, v4.y, v4.z) # 3 3 3

Также можно перегрузить другие операторы, например, операторы сравнения (<, >, <=, >=, ==, !=), операторы индексации ([]) и т.д.

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

Имена методов для перегрузки стандартных операторов

Также можно перегрузить другие операторы, например, операторы сравнения (<, >, <=, >=, ==, !=), операторы индексации ([]) и т.д.

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

  • __add__(self, other) для оператора сложения (+)
  • __sub__(self, other) для оператора вычитания (-)
  • __mul__(self, other) для оператора умножения (*)
  • __truediv__(self, other) для оператора деления (/)
  • __mod__(self, other) для оператора остатка от деления (%)
  • __pow__(self, other) для оператора возведения в степень (**)
  • __lt__(self, other) для оператора меньше (<)
  • __le__(self, other) для оператора меньше или равно (<=)
  • __eq__(self, other) для оператора равенства (==)
  • __ne__(self, other) для оператора неравенства (!=)
  • __gt__(self, other) для оператора больше (>)
  • __ge__(self, other) для оператора больше или равно (>=)

Эти методы должны быть определены в классе, который перегружает соответствующий оператор. Они принимают два аргумента — self и other, где self представляет объект, на котором вызывается оператор, а other представляет объект, с которым выполняется операция. Возвращаемое значение этих методов должно быть результатом выполнения соответствующей операции.

Перегрузка операторов индексации

Перегрузка оператора индексации [] позволяет создавать собственные объекты, которые можно обращать, как к элементам массива, используя квадратные скобки.

Пример реализации класса, который использует перегрузку оператора индексации:

class MyList:
    def __init__(self, items):
        self.items = items

    def __getitem__(self, index):
        return self.items[index]

    def __setitem__(self, index, value):
        self.items[index] = value

my_list = MyList([1, 2, 3])
print(my_list[0])  # Output: 1
my_list[0] = 4
print(my_list[0])  # Output: 4

В данном примере класс MyList имеет атрибут items, который хранит элементы списка. Перегрузка оператора __getitem__ позволяет обращаться к элементам списка через квадратные скобки, как если бы объект MyList был списком. Перегрузка оператора __setitem__ позволяет изменять элементы списка через оператор присваивания =.

В данном примере my_list[0] вернет значение 1, а затем его значение будет изменено на 4 через оператор my_list[0] = 4. После этого my_list[0] вернет значение 4.

Пример реализации ООП

Рассмотрим следующие классы:

import math

# Абстрактный класс Фигура, содержит абстрактные методы для наследников
class Figure:
    def area(self):
        pass

    def perimeter(self):
        pass

# Класс Круг, является наследником класса Фигура
class Circle(Figure):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return math.pi * self.radius ** 2

    def perimeter(self):
        return 2 * math.pi * self.radius

# Класс Прямоугольник, является наследником класса Фигура
class Rectangle(Figure):
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        return self.length * self.width

    def perimeter(self):
        return 2 * (self.length + self.width)

# Класс Квадрат, является наследником класса Прямоугольник, используется полиморфизм
class Square(Rectangle):
    def __init__(self, side):
        super().__init__(side, side)

# Класс Треугольник, является наследником класса Фигура
class Triangle(Figure):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    def area(self):
        s = (self.a + self.b + self.c) / 2
        return math.sqrt(s * (s - self.a) * (s - self.b) * (s - self.c))

    def perimeter(self):
        return self.a + self.b + self.c

# Пример использования классов
circle = Circle(5)
print("Площадь круга: ", circle.area())
print("Периметр круга: ", circle.perimeter())

rectangle = Rectangle(3, 4)
print("Площадь прямоугольника: ", rectangle.area())
print("Периметр прямоугольника: ", rectangle.perimeter())

square = Square(5)
print("Площадь квадрата: ", square.area())
print("Периметр квадрата: ", square.perimeter())

triangle = Triangle(3, 4, 5)
print("Площадь треугольника: ", triangle.area())
print("Периметр треугольника: ", triangle.perimeter())

В данном примере использованы все основные принципы ООП:

  • Наследование: классы Круг, Прямоугольник, Квадрат, Треугольник являются наследниками абстрактного класса Фигура.
  • Инкапсуляция: данные, необходимые для расчетов (радиус, стороны), хранятся внутри объектов классов, и доступ к ним осуществляется через методы.
  • Полиморфизм: методы area и perimeter рассчитывают площадь и периметр, исходя из особенностей фигуры
  • Абстракция: класс Figure — абстрактный, его методы не работают напрямую и созданы для реализации классов-потомков.

Также мы можем использовать полиморфизм для вывода информации о фигурах:

def print_shape_info(shape):
    print("Area: ", shape.area())
    print("Perimeter: ", shape.perimeter())

circle = Circle( 5)
rectangle = Rectangle(4, 6)

print_shape_info(circle)
print_shape_info(rectangle)

В этом примере мы определяем функцию print_shape_info(), которая принимает объект класса Figure. Эта функция выводит информацию о цвете, площади и периметре фигуры. Затем мы создаем объекты классов Circle и Rectangle и вызываем функцию print_shape_info() для каждого из них. Заметьте, что мы можем передавать объекты разных классов в эту функцию, потому что они оба наследуют класс Shape и реализуют методы area() и perimeter().

Также мы можем использовать перегрузку операторов для сравнения фигур:

class Circle(Shape):
    #...

    def __lt__(self, other):
        return self.radius < other.radius

class Rectangle(Shape):
    #...

    def __lt__(self, other):
        return self.area() < other.area()

circle1 = Circle(5)
circle2 = Circle( 3)
rectangle1 = Rectangle(4, 6)
rectangle2 = Rectangle(3, 5)

print(circle1 < circle2) # False
print(rectangle1 < rectangle2) # False
print(circle1 < rectangle1) # TypeError: '<' not supported between instances of 'Circle' and 'Rectangle'

Здесь мы определяем метод __lt__(), который позволяет сравнивать объекты

Задания для подготовки

Задание 1. Создайте классы для представления различных геометрических тел, таких как сфера, параллелепипед, конус и т.д. Используйте принцип наследования для создания родительского класса, который будет содержать общие атрибуты и методы для всех тел, например, объем и площадь поверхности. Затем создайте дочерние классы, которые наследуют эти атрибуты и методы и добавляют дополнительные, специфичные для каждого тела.

Решение:

Для решения данной задачи можно создать базовый класс GeometricSolid со следующими общими атрибутами и методами:

  • volume() — метод для вычисления объема тела
  • surface_area() — метод для вычисления площади поверхности тела

Затем можно создать дочерние классы, которые будут наследовать от GeometricSolid и добавлять свои собственные атрибуты и методы для конкретных геометрических тел.

Пример реализации:

import math

class GeometricSolid:
    def volume(self):
        pass
    
    def surface_area(self):
        pass

class Sphere(GeometricSolid):
    def __init__(self, radius):
        self.radius = radius
        
    def volume(self):
        return 4/3 * math.pi * self.radius**3
    
    def surface_area(self):
        return 4 * math.pi * self.radius**2

class Cube(GeometricSolid):
    def __init__(self, length):
        self.length = length
        
    def volume(self):
        return self.length**3
    
    def surface_area(self):
        return 6 * self.length**2

class Cylinder(GeometricSolid):
    def __init__(self, radius, height):
        self.radius = radius
        self.height = height
        
    def volume(self):
        return math.pi * self.radius**2 * self.height
    
    def surface_area(self):
        return 2 * math.pi * self.radius * self.height + 2 * math.pi * self.radius**2

В этом примере мы создали три дочерних класса: Sphere, Cube, и Cylinder. В каждом из них мы определили конструктор, который принимает необходимые аргументы для вычисления объема и площади поверхности. Затем мы переопределили методы volume() и surface_area() для каждого класса, чтобы они возвращали значения, соответствующие конкретному геометрическому телу.

Пример использования:

sphere = Sphere(5)
print("Sphere radius:", sphere.radius)
print("Sphere volume:", sphere.volume())
print("Sphere surface area:", sphere.surface_area())

cube = Cube(3)
print("Cube length:", cube.length)
print("Cube volume:", cube.volume())
print("Cube surface area:", cube.surface_area())

cylinder = Cylinder(2, 5)
print("Cylinder radius:", cylinder.radius)
print("Cylinder height:", cylinder.height)
print("Cylinder volume:", cylinder.volume())
print("Cylinder surface area:", cylinder.surface_area())

Результат выполнения:

Sphere radius: 5
Sphere volume: 523.5987755982989
Sphere surface area: 314.1592653589793
Cube length: 3
Cube volume: 27
Cube surface area: 54
Cylinder radius: 2
Cylinder height: 5
Cylinder volume: 62.83185307179586
Cylinder surface area: 62.83185307179586

Задание 2. Создайте классы для представления различных животных, например, кошек, собак, птиц и т.д. Используйте принцип наследования для создания родительского класса, который будет содержать общие атрибуты и методы для всех животных, например, возраст и цвет шерсти. Затем создайте дочерние классы, которые наследуют эти атрибуты и методы и добавляют дополнительные, специфичные для каждого вида животных, например, способность летать или область обитания.

Решение:
class Animal:
    def __init__(self, age, color):
        self.age = age
        self.color = color
    
    def speak(self):
        print("I am an animal")
        

class Cat(Animal):
    def __init__(self, age, color, breed):
        super().__init__(age, color)
        self.breed = breed
    
    def speak(self):
        print("Meow")
        

class Dog(Animal):
    def __init__(self, age, color, breed):
        super().__init__(age, color)
        self.breed = breed
    
    def speak(self):
        print("Woof")
        

class Bird(Animal):
    def __init__(self, age, color, wingspan):
        super().__init__(age, color)
        self.wingspan = wingspan
    
    def speak(self):
        print("Chirp")
        

cat = Cat(2, "black", "siamese")
dog = Dog(4, "brown", "golden retriever")
bird = Bird(1, "green", 12)

print(cat.age)         # Output: 2
print(dog.breed)       # Output: golden retriever
print(bird.color)      # Output: green

cat.speak()            # Output: Meow
dog.speak()            # Output: Woof
bird.speak()           # Output: Chirp

В этом примере мы создали базовый класс Animal, который содержит общие атрибуты и методы для всех животных, такие как возраст и цвет шерсти, а также метод speak, который печатает звук, который производит животное. Затем мы создали три дочерних класса: Cat, Dog и Bird, которые наследуют от Animal и добавляют свои собственные атрибуты и методы, например, порода для кошек и собак и размах крыльев для птиц. Каждый класс также переопределяет метод speak, чтобы выводить свой собственный звук.

В конце мы создали объекты каждого класса и вызвали их методы и атрибуты.

Задание 3. Создайте классы для представления различных транспортных средств, например, автомобилей, велосипедов, самолетов и т.д. Используйте принцип наследования для создания родительского класса, который будет содержать общие атрибуты и методы для всех транспортных средств, например, скорость и вместимость. Затем создайте дочерние классы, которые наследуют эти атрибуты и методы и добавляют дополнительные, специфичные для каждого транспортного средства, например, тип двигателя или способность летать.

Решение:
class Transport:
    def __init__(self, speed, capacity):
        self.speed = speed
        self.capacity = capacity

    def move(self):
        print(f"Moving at {self.speed} km/h")

    def load(self, amount):
        if amount <= self.capacity:
            print(f"Loading {amount} passengers")
        else:
            print(f"Not enough capacity to load {amount} passengers")


class Car(Transport):
    def __init__(self, speed, capacity, engine_type):
        super().__init__(speed, capacity)
        self.engine_type = engine_type

    def park(self):
        print("Parking the car")

    def honk(self):
        print("Honking the horn")


class Bicycle(Transport):
    def __init__(self, speed, capacity, frame_material):
        super().__init__(speed, capacity)
        self.frame_material = frame_material

    def ride(self):
        print("Riding the bicycle")

    def ring_bell(self):
        print("Ringing the bell")


class Airplane(Transport):
    def __init__(self, speed, capacity, engine_type, wingspan):
        super().__init__(speed, capacity)
        self.engine_type = engine_type
        self.wingspan = wingspan

    def fly(self):
        print("Flying the airplane")

    def land(self):
        print("Landing the airplane")

Здесь класс Transport является родительским классом для всех транспортных средств и содержит общие атрибуты и методы для них, такие как скорость и вместимость.

Затем мы создали три дочерних класса: Car, Bicycle и Airplane. Класс Car добавляет атрибут engine_type и методы park и honk. Класс Bicycle добавляет атрибут frame_material и методы ride и ring_bell. Класс Airplane добавляет атрибуты engine_type и wingspan и методы fly и land.

Теперь мы можем создавать объекты каждого класса и вызывать их методы, как показано ниже:

car = Car(100, 5, "gasoline")
car.move() # Moving at 100 km/h
car.load(3) # Loading 3 passengers
car.park() # Parking the car
car.honk() # Honking the horn

bicycle = Bicycle(20, 1, "steel")
bicycle.move() # Moving at 20 km/h
bicycle.load(1) # Loading 1 passengers
bicycle.ride() # Riding the bicycle
bicycle.ring_bell() # Ringing the bell

airplane = Airplane(800, 200, "jet", 40)
airplane.move() # Moving at 800 km/h
airplane.load(150) # Loading 150 passengers
airplane.fly() # Flying the airplane
airplane.land() # Landing the airplane

Задание 4. Создайте классы для представления различных продуктов в магазине, например, книг, музыки, электроники и т.д. Используйте принцип наследования для создания родительского класса, который будет содержать общие атрибуты и методы для всех продуктов, например, цена и описание. Затем создайте дочерние классы, которые наследуют эти атрибуты и методы и добавляют дополнительные, специфичные для каждого продукта, например, автора или продолжительность воспроизведения.

Решение:
class Product:
    def __init__(self, name, price, description):
        self.name = name
        self.price = price
        self.description = description
        
    def display_info(self):
        print(f"{self.name} - {self.price} руб. ({self.description})")


class Book(Product):
    def __init__(self, name, price, description, author, pages):
        super().__init__(name, price, description)
        self.author = author
        self.pages = pages
        
    def display_info(self):
        super().display_info()
        print(f"Автор: {self.author}, Количество страниц: {self.pages}")


class Music(Product):
    def __init__(self, name, price, description, artist, duration):
        super().__init__(name, price, description)
        self.artist = artist
        self.duration = duration
        
    def display_info(self):
        super().display_info()
        print(f"Исполнитель: {self.artist}, Продолжительность: {self.duration}")


class Electronics(Product):
    def __init__(self, name, price, description, manufacturer, power):
        super().__init__(name, price, description)
        self.manufacturer = manufacturer
        self.power = power
        
    def display_info(self):
        super().display_info()
        print(f"Производитель: {self.manufacturer}, Мощность: {self.power} Вт")

В этом примере мы создали родительский класс Product, который содержит общие атрибуты и методы для всех продуктов. Затем мы создали дочерние классы Book, Music и Electronics, которые наследуют эти атрибуты и методы, а также добавляют специфичные для каждого продукта атрибуты и методы.

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

Например, мы можем создать экземпляры классов и вызвать их методы display_info:

book = Book("Война и мир", 1500, "Роман Л. Н. Толстого", "Лев Толстой", 1225)
book.display_info()

music = Music("Stairway to Heaven", 99, "Культовая песня группы Led Zeppelin", "Led Zeppelin", "8:02")
music.display_info()

electronics = Electronics("Samsung Galaxy S21", 69990, "Смартфон", "Samsung", 25)
electronics.display_info()

Результат

Война и мир - 1500 руб. (Роман Л. Н. Толстого)
Автор: Лев Толстой, Количество страниц: 1225
Stairway to Heaven - 99 руб. (Культовая песня группы Led Zeppelin)
Исполнитель: Led Zeppelin, Продолжительность: 8:02
Samsung Galaxy S21 - 69990 руб. (Смартфон)
Производитель: Samsung

 

Понравилась статья? Поделиться с друзьями:
Школа Виктора Комлева
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!:

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.