Советы

Абстракция и полиморфизм в Python на примерах: код и задачи

Всё объектно-ориентированное программирование строится на четырёх понятиях: инкапсуляции, наследовании, полиморфизме и абстракциях. Поэтому давайте объявим наш класс «Кошка» и будем объяснять ООП на нём:

class Cat(): def __init__(self, breed, color, age): self.breed = breed self.color = color self.age = age def meow(self): print('Мяу!') def purr(self): print('Мрррр')

Метод __init__ — инициализатор класса. Он вызывается сразу после создания объекта, чтобы присваивать значения динамическим атрибутам. self — ссылка на текущий объект, она даёт доступ к атрибутам и методам, с которыми вы работаете. Её аналог в других языках программирования — this.

Примечание 1. Слово self общепринятое, но не обязательное, вместо него можно использовать любое другое. Однако это может запутать тех, кто будет читать ваш код.

Примечание 2. Названия классов принято писать с прописной буквы, а объектов — со строчной.

Итак, мы создали класс Cat, в котором объявили три атрибута: порода — breed, цвет — color и возраст — age. А ещё добавили два метода, чтобы наша кошка умела мяукать — meow() и мурчать — purr().

Давайте создадим пару объектов нашего класса:

cat1 = Cat('Абиссинская', 'Рыжая', 4)
cat2 = Cat('Британская', 'Серая', 2)

Отлично, теперь, когда у нас есть основа, приступим к изучению принципов ООП.

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

Вернёмся к нашим кошечкам. Мы можем разрешить изменять атрибут «возраст», но только в большую сторону, а атрибуты «порода» и «цвет» лучше открыть только для чтения — ведь порода кошки не меняется, а цвет если и меняется, то не по её инициативе.

В нашем классе «Кошка» мы сделали все атрибуты открытыми, поэтому давайте это исправим:

class Cat(): def __init__(self, breed, color, age): self._breed = breed self._color = color self._age = age def breed(self): return self._breed def color(self): return self._color def age(self): return self._age def age(self, new_age): if new_age > self._age: self._age = new_age return self._age

Код стал выглядеть немного сложнее, но мы сейчас всё объясним. Сначала мы сделали все атрибуты закрытыми с помощью символа _. Он говорит интерпретатору, что эта переменная будет доступна только внутри методов класса.

Нам всё ещё нужно получать доступ к атрибутам, поэтому мы предоставляем его через @property и объявляем для каждого атрибута свой метод — breed, color, age. В каждом из этих методов мы возвращаем значение нашего закрытого атрибута. Это делает его доступным только для чтения.

И последнее — мы должны позволить пользователям увеличивать возраст кота. Для этого воспользуемся @age.setter и ещё раз объявим метод age, а внутри него напишем простое условие и вернём значение атрибута.

Теперь создадим экземпляр класса:

cat = Cat('Абиссинская', 'Рыжая', 4)

Выведем значения атрибутов:

print(cat.breed)
print(cat.color)
print(cat.age)

И попробуем изменить атрибут age:

cat.age = 5
print(cat.age)

Всё успешно. А теперь сделаем это с другим атрибутом:

cat.breed = 'Сиамская'
print(cat.breed)

Мы получили ошибку, потому что запретили изменять этот атрибут.

Абстракция и полиморфизм в Python на примерах: код и задачи

Инкапсуляция, модификаторы доступа: 3 часть гайда по ООП

Классы могут передавать свои атрибуты и методы классам-потомкам. Например, мы хотим создать новый класс «Домашняя кошка». Он практически идентичен классу «Кошка», но у него появляются новые атрибуты «хозяин» и «кличка», а также метод «клянчить вкусняшку».

Достаточно объявить «Домашнюю кошку» наследником «Кошки» и прописать новые атрибуты и методы — вся остальная функциональность перейдёт от родителя к потомку.

https://www.youtube.com/watch?v=fzUI3NyJflw\u0026pp=ygVm0JDQsdGB0YLRgNCw0LrRhtC40Y8g0Lgg0L_QvtC70LjQvNC-0YDRhNC40LfQvCDQsiBQeXRob24g0L3QsCDQv9GA0LjQvNC10YDQsNGFOiDQutC-0LQg0Lgg0LfQsNC00LDRh9C4

Давайте объявим новый класс:

class HomeCat(Cat): def __init__(self, breed, color, age, owner, name): super().__init__(breed, color, age) self._owner = owner self._name = name def owner(self): return self._owner def name(self): return self._name def getTreat(self): print('Мяу-мяу')

В первой строке мы как раз наследуем все методы и атрибуты класса Cat. А чтобы всё создалось корректно, мы должны вызвать метод super() в методе __init__() и через него заполнить атрибуты класса-родителя. Поэтому мы и передаём в этот метод «породу», «окрас» и «возраст».

Кроме атрибутов для класса-родителя у класса-потомка есть и собственные атрибуты: «хозяин» — owner и «кличка» — name. Их мы будем использовать только в этом классе, поэтому они будут недоступны для класса-родителя.

Мы сразу сделали атрибуты класса-потомка закрытыми и объявили для них собственные методы. А также добавили метод ​​getTreat(), которого нет в классе-родителе.

Давайте создадим объект класса:

my_cat = HomeCat('Сиамская', 'Белая', 3, 'Иван', 'Роза')

print(my_cat.owner)
print(my_cat.breed)
my_cat.getTreat()
my_cat.purr()

Как видим, у нас работают и новые методы, и старые. Наследование прошло успешно.

Абстракция и полиморфизм в Python на примерах: код и задачи

Наследование и ещё немного полиморфизма: 5 часть гайда по ООП

Этот принцип позволяет применять одни и те же команды к объектам разных классов, даже если они выполняются по-разному.

Например, помимо класса «Кошка», у нас есть никак не связанный с ним класс «Попугай» — и у обоих есть метод «спать».

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

Допустим у нас есть два класса — «Кошка» и «Попугай»:

class Cat: def sleep(self): print('Свернулся в клубок и сладко спит.') class Parrot: def sleep(self): print('Сел на жёрдочку и уснул.')

А теперь пусть у нас есть метод, который ожидает, что ему на вход придёт объект, у которого будет метод sleep:

def homeSleep(animal):
animal.sleep()

Посмотрим, как это будет работать:

cat = Cat()
parrot = Parrot()
homeSleep(cat)
homeSleep(parrot)

Хотя классы разные, их одноимённые методы работают похожим образом. Это и есть полиморфизм.

Абстракция и полиморфизм в Python на примерах: код и задачи

Полиморфизм в ООП, перегрузка методов и операторов

При создании класса мы упрощаем его до тех атрибутов и методов, которые нужны именно в этом коде, не пытаясь описать его целиком и отбрасывая всё второстепенное. Например, у всех хищников есть метод «охотиться», поэтому все животные, которые являются хищниками, автоматически будут уметь охотиться.

Рассмотрим класс Predator:

class Predator: def hunt(self): print('Охотится…')

Этот класс будет общим для всех животных, которые являются хищниками, — например, кошек:

class Cat(Predator): def __init__(self, name, color): super().__init__() self._name = name self._color = color def name(self): return self._name def color(self): return self._color

У кошки есть свои атрибуты: «имя» — name и «окрас» — color. Но при этом она потомок хищников, а значит, умеет охотиться:

cat = Cat('Даниэла', 'Чёрный')
cat.hunt() Абстракция и полиморфизм в Python на примерах: код и задачи

Абстрактные классы и интерфейсы: 6 часть гайда по ООП

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

Популярный язык программирования Python также следует парадигме объектно-ориентированного программирования. Объявление классов и объектов в Python закладывают основу концепций ООП.

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

Объектно-ориентированное программирование в Python – это способ компьютерного программирования, использующий идею «объектов» для представления данных и методов. Это также подход, используемый для создания аккуратного и многоразового кода вместо избыточного.

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

Что такое концепции ООП?

Основные концепции ООП (объектно-ориентированного программирования) в Python включают класс, объект, метод, наследование, полиморфизм, абстракцию данных и инкапсуляцию.

Что такое классы и объекты?

Класс – это набор объектов или план объектов, определяющих общие атрибуты и поведение. Возникает вопрос, как это сделать?

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

Я могу привести вам пример из реальной жизни – представьте, что офис становится «сотрудником» как класс и все атрибуты, связанные с ним, такие как «emp_name», «emp_age», «emp_salary», «emp_id» как объекты в Python. Давайте посмотрим с точки зрения кодирования, как создать экземпляр класса и объекта.

Класс определяется ключевым словом «Класс». Пример:

class class1(): // class 1 is the name of the class

Примечание. Python не чувствителен к регистру.

Объекты

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

Синтаксис: obj = class1()

Здесь, obj – это «объект» class1.

Создание объекта и класса

Пример:

class employee():
def __init__(self,name,age,id,salary): //creating a function
self.name = name // self is an instance of a class
self.age = age
self.salary = salary
self.id = id

emp1 = employee(«harshit»,22,1000,1234) //creating objects
emp2 = employee(«arjun»,23,2000,2234)
print(emp1.__dict__)//Prints dictionary

Объяснение: ’emp1′ и ’emp2′ – это объекты, экземпляры которых создаются для класса ’employee’. Здесь слово (__dict__) – это «словарь», который печатает все значения объекта ’emp1′ для данного параметра ( имя, возраст, зарплата). (__ init__) действует как конструктор, который вызывается всякий раз, когда создается объект.

Я надеюсь, что теперь вы, ребята, не столкнетесь с какими-либо проблемами при работе с «классами» и «объектами» в будущем.

Методологии объектно-ориентированного программирования

Методологии объектно-ориентированного программирования имеют дело со следующими концепциями:

  • наследование;
  • полиморфизм;
  • инкапсуляция;
  • абстракция.

Давайте подробно разберемся с первой концепцией наследования.

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

Вы когда-нибудь слышали об этом диалоге от родственников: «Вы выглядите в точности как отец или мать», причина этого называется «наследство».

С точки зрения программирования это обычно означает «наследование или передачу характеристик от родительского к дочернему классу без каких-либо изменений».

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

Абстракция и полиморфизм в Python на примерах: код и задачи

Разберемся подробно с каждой из подтем.

Одиночное наследование

Одноуровневое наследование позволяет производному классу наследовать характеристики от одного родительского класса.

Пример:

class employee1()://This is a parent class
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary

class childemployee(employee1)://This is a child class
def __init__(self, name, age, salary,id):
self.name = name
self.age = age
self.salary = salary
self.id = id
emp1 = employee1('harshit',22,1000)

print(emp1.age)

  • Выход: 22.
  • Объяснение:
  • Я взял родительский класс и создал конструктор (__init__), сам класс инициализирует атрибуты параметрами («имя», «возраст» и «зарплата»).
  • Создал дочерний класс childemployee, который наследует свойства родительского класса, и, наконец, созданы экземпляры объектов emp1 и emp2 в соответствии с параметрами.
  • Я напечатал возраст emp1. Что ж, вы можете распечатать весь словарь, имя или зарплату.

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

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

Пример:

class employee()://Super class
def __init__(self,name,age,salary):
self.name = name
self.age = age
self.salary = salary
class childemployee1(employee)://First child class
def __init__(self,name,age,salary):
self.name = name
self.age = age
self.salary = salary

class childemployee2(childemployee1)://Second child class
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
emp1 = employee('harshit',22,1000)
emp2 = childemployee1('arjun',23,2000)

print(emp1.age)
print(emp2.age)

  1. Выход: 22,23.
  2. Объяснение:
  • Это четко объясняется в коде, написанном выше. Здесь я определил суперкласс как сотрудник, а дочерний класс, как childemployee1. Теперь childemployee1 действует, как родитель для childemployee2.
  • Я создал два объекта «emp1» и «emp2», где я передаю параметры «имя», «возраст», «зарплата» для emp1 из суперкласса «сотрудник» и «имя», «возраст», «зарплата» и «идентификатор». Из родительского класса «childemployee1 »

Иерархическое наследование

Наследование на иерархическом уровне позволяет более чем одному производному классу наследовать свойства родительского класса.

Пример:

class employee():
def __init__(self, name, age, salary): //Hierarchical Inheritance
self.name = name
self.age = age
self.salary = salary

class childemployee1(employee):
def __init__(self,name,age,salary):
self.name = name
self.age = age
self.salary = salary

class childemployee2(employee):
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
emp1 = employee('harshit',22,1000)
emp2 = employee('arjun',23,2000)

print(emp1.age)
print(emp2.age)

  • Выход: 22,23.
  • Объяснение:
  • В приведенном выше примере вы можете ясно видеть, что существует два дочерних класса «childemployee1» и «childemployee2». Они наследуют функции от общего родительского класса, который называется «служащий».
  • Объекты emp1 и emp2 создаются по параметрам name, age, salary.

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

Многоуровневое наследование позволяет производному классу наследовать свойства более чем одного базового класса.

Читайте также:  LlamaIndex: создаем чат-бота с помощью ретривера BM25Retriever. Часть 3

Пример:

class employee1()://Parent class
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary

class employee2()://Parent class
def __init__(self,name,age,salary,id):
self.name = name
self.age = age
self.salary = salary
self.id = id

class childemployee(employee1,employee2):
def __init__(self, name, age, salary,id):
self.name = name
self.age = age
self.salary = salary
self.id = id
emp1 = employee1('harshit',22,1000)
emp2 = employee2('arjun',23,2000,1234)

print(emp1.age)
print(emp2.id)

Выход: 22,1234.

Объяснение: В приведенном выше примере я взял два родительских класса «employee1» и «employee2». И дочерний класс «childemployee», который наследует оба родительских класса, создавая экземпляры объектов emp1 и emp2 по параметрам родительские классы.

Речь шла о наследовании, продвигаясь вперед в объектно-ориентированном программировании Python, давайте углубимся в «полиморфизм».

Полиморфизм

Вы все должны были использовать GPS для навигации по маршруту.

Разве не удивительно, сколько разных маршрутов вы встречаете для одного и того же пункта назначения в зависимости от трафика, с точки зрения программирования это называется «полиморфизмом».

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

Полиморфизм бывает двух типов:

  • Полиморфизм времени компиляции
  • Полиморфизм времени выполнения

Полиморфизм времени компиляции

Полиморфизм времени компиляции, также называемый статическим полиморфизмом, разрешается во время компиляции программы. Один из распространенных примеров – «перегрузка метода». Позвольте мне показать вам быстрый пример того же.

Пример:

class employee1():
def name(self):
print(«Harshit is his name»)
def salary(self):
print(«3000 is his salary»)

def age(self):
print(«22 is his age»)

class employee2():
def name(self):
print(«Rahul is his name»)

def salary(self):
print(«4000 is his salary»)

def age(self):
print(«23 is his age»)

def func(obj)://Method Overloading
obj.name()
obj.salary()
obj.age()

obj_emp1 = employee1()
obj_emp2 = employee2()

func(obj_emp1)
func(obj_emp2)

  1. Вывод:
  2. Его зовут Харшит, 3000 – его зарплата,  22 – его возраст; его зовут Рахул, 4000 – его зарплата, 23 – его возраст.
  3. Объяснение:
  • В приведенной выше программе я создал два класса «employee1» и «employee2», создал функции для «name», «salary» и «age» и распечатал их значения, не принимая их у пользователя.
  • Теперь добро пожаловать в основную часть, где я создал функцию с параметром «obj» и вызвал все три функции, то есть «имя», «возраст» и «зарплата».
  • Позже были созданы экземпляры объектов emp_1 и emp_2 для двух классов и просто вызвана функция. Такой тип называется перегрузкой метода, который позволяет классу иметь более одного метода с одним и тем же именем.

Полиморфизм времени выполнения

Полиморфизм времени выполнения также называется динамическим полиморфизмом, где он разрешается во время выполнения. Одним из распространенных примеров полиморфизма времени выполнения является «переопределение метода». Позвольте мне показать вам пример для лучшего понимания.

Пример:

class employee():
def __init__(self,name,age,id,salary):
self.name = name
self.age = age
self.salary = salary
self.id = id
def earn(self):
pass

class childemployee1(employee):

def earn(self)://Run-time polymorphism
print(«no money»)

class childemployee2(employee):

def earn(self):
print(«has money»)

c = childemployee1
c.earn(employee)
d = childemployee2
d.earn(employee)

Выход: денег нет, деньги есть.

Объяснение: В приведенном выше примере я создал два класса childemployee1 и childemployee2, которые являются производными от одного и того же базового класса employee. Один не получил денег, а другой получил.

Теперь главный вопрос: как это произошло? Итак, если вы присмотритесь, я создал пустую функцию и использовал Pass (оператор, который используется, когда вы не хотите выполнять какую-либо команду или код).

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

Переходя к следующей методологии объектно-ориентированного программирования Python, я расскажу об инкапсуляции.

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

В необработанном виде инкапсуляция означает связывание данных в одном классе. В Python, в отличие от Java, нет закрытых ключевых слов. Доступ к классу не должен осуществляться напрямую, он должен иметь префикс в виде подчеркивания.

Пример:

class employee(object):
def __init__(self):
self.name = 1234
self._age = 1234
self.__salary = 1234

object1 = employee()
print(object1.name)
print(object1._age)
print(object1.__salary)

Вывод:

1234 Traceback (последний вызов последний): 1234 Файл «C: /Users/Harshit_Kant/PycharmProjects/test1/venv/encapsu.py», строка 10, в печати (object1 .__ salary) AttributeError: объект ’employee’ не имеет атрибута ‘ __зарплата’

Объяснение: Вы получите этот вопрос, что такое подчеркивание и ошибка? Класс python обрабатывает частные переменные, как (__ salary), к которым нельзя получить доступ напрямую.

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

Пример:

class employee():
def __init__(self):
self.__maxearn = 1000000
def earn(self):
print(«earning is:{}».format(self.__maxearn))

def setmaxearn(self,earn)://setter method used for accesing private class
self.__maxearn = earn

emp1 = employee()
emp1.earn()

emp1.__maxearn = 10000
emp1.earn()

emp1.setmaxearn(10000)
emp1.earn()

Вывод: Заработок: 1000000, заработок: 1000000, заработок: 10000.

Объяснение: Использование метода установки обеспечивает косвенный доступ к методу частного класса. Здесь я определил класс сотрудника и использовал (__maxearn), который представляет собой метод установки, используемый здесь для хранения максимального заработка сотрудника, и функцию установки setmaxearn(), которая принимает цену в качестве параметра.

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

Далее в методологии объектно-ориентированного программирования Python рассказывается об одной из ключевых концепций, называемой абстракцией.

Абстракция

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

Пример:

from abc import ABC,abstractmethod
class employee(ABC):
def emp_id(self,id,name,age,salary): //Abstraction
pass

class childemployee1(employee):
def emp_id(self,id):
print(«emp_id is 12345»)

emp1 = childemployee1()
emp1.emp_id(id)

Вывод: emp_id – 12345.

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

Python не имеет спецификаторов доступа, таких как «частный», как в java. Он поддерживает большинство терминов, связанных с «объектно-ориентированным» языком программирования, за исключением строгой инкапсуляции. Следовательно, он не полностью объектно-ориентирован.

Основы ООП в Python — классы, объекты, методы

ООП — самая используемая парадигма программирования. Это одновременно и особый способ мышления, и отдельная методика. Её концепцию проще всего понимать на примерах из реальной жизни. И это неспроста. Объектно-ориентированное программирование помогает представлять содержимое программы наиболее естественным для нашего мира способом.

https://www.youtube.com/watch?v=fzUI3NyJflw\u0026pp=YAHIAQE%3D

Главным понятием ООП является понятие программного объекта. Вообще говоря, большинство сущностей на планете Земля — это некие объекты. И с частью из них мы взаимодействуем при помощи программирования.

Банковский счёт, персонаж компьютерной игры или анимированный виджет сайта — всё это легко представить в виде объектов.

Можно сказать, что объектно-ориентированное программирование позволяет смоделировать реальный объект в виде программного.

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

????‍♂️ Итак, мы — разработчики игр. Наша студия трудится над новым автосимулятором. В игре будут представлены разные виды транспорта: легковые автомобили, гоночные, грузовые и пассажирские.

Все их можно описать одним словом — автотранспорт. Сделав это, мы абстрагировались от деталей и, таким образом, определили класс.

Объектом этого класса может быть, как Бьюик 1968-го года, так и грузовой Freightliner Columbia желтого цвета.

У класса есть свойства и функции (в ООП их называют методами).

  • Свойства — это характеристики, присущие данному конкретному множеству объектов.
  • Методы — те действия, которые они могут совершать.

Свойствами класса «автотранспорт» могут быть, например: год выпуска, вид и цвет. На уровне объектов это будет выглядеть так: Бьюик Электра — это объект класса «Автотранспорт» со следующими свойствами:

  • вид — легковой автомобиль;
  • цвет — чёрный;
  • год выпуска — 1968.

Можно сказать, что объект — это вполне конкретный экземпляр класса

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

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

В Питоне класс «Автотранспорт» может выглядеть так:

# класс автотранспорт
class MotorTransport(object):
def __init__(self, color, year, auto_type):
self.color = color
self.year = year
self.auto_type = auto_type

# тормозить
def stop(self):
print(«Pressing the brake pedal»)

# ехать
def drive(self):
print('WRRRRRUM!')

Теперь никто не помешает нам получить собственную красную феррари. Пусть и в симуляторе.

# создадим объект класса Автотранспорт
ferrari_testarossa = MotorTransport('Red', 1987, 'passenger car')
# жмём на газ и вперёд!
ferrari_testarossa.drive()

> WRRRRRUM!

Принципы ООП

Абстракция

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

Любой составной объект реального мира — это абстракция. Говоря «ноутбук», вам не требуется дальнейших пояснений, вроде того, что это организованный набор пластика, металла, жидкокристаллического дисплея и микросхем.

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

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

Полиморфизм

Полиморфизм подразумевает возможность нескольких реализаций одной идеи. Простой пример: у вас есть класс «Персонаж», а у него есть метод «Атаковать». Для воина это будет означать удар мечом, для рейнджера — выстрел из лука, а для волшебника — чтение заклинания «Огненный Шар». В сущности, все эти три действия — атака, но в программном коде они будут реализованы совершенно по-разному.

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

Это способность одного класса расширять понятие другого, и главный механизм повторного использования кода в ООП. Вернёмся к нашему автосимулятору.

На уровне абстракции «Автотранспорт» мы не учитываем особенности каждого конкретного вида транспортного средства, а рассматриваем их «в целом».

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

Мы могли бы сделать отдельный класс «Грузовик», который является наследником «Автотранспорта». Объекты этого класса могли бы определять все прошлые атрибуты (цвет, год выпуска), но и получить новые.

Для грузовиков это могли быть грузоподъёмность, снаряженная масса и наличие жилого отсека в кабине. А методом, который есть только у грузовиков, могла быть функция сцепления и отцепления прицепа.

Читайте также:  ТОП-10 книг по JavaScript в 2023 году

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

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

Вы разработали для муниципальных служб класс «Квартира». У неё есть свойства вроде адреса, метража и высоты потолков. И методы, такие как получение информации о каждом из этих свойств и, главное, метод, реализующий постановку на учёт в Росреестре.

Это готовая концепция, и вам не нужно чтобы кто-то мог добавлять методы «открыть дверь» и «получить место хранения денег». Это А) Небезопасно и Б) Избыточно, а также, в рамках выбранной реализации, не нужно.

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

Класс

— У тебя есть ключ?
— Лучше! У меня есть рисунок ключа!

Классы, в некотором смысле, подобны чертежам: это не объекты сами по себе, а их схемы. Класс «банковских счетов» имеет строго определенные и одинаковые для всех атрибуты, но объекты в нём — сами счета — уникальны.

Как в Python создать класс

В Python классы и объекты по смыслу не отличаются от других языков. Нюансы в реализации. Для создания класса в Питоне необходимо написать инструкцию class, а затем выбрать имя. В простейшем случае, класс выглядит так:

class SimpleClass:
pass

Для именования классов в Python обычно используют стиль «camel case», где первая буква — заглавная.

Ооп в python: классы и объекты

okpython.net

Все для начинающих

Понятия класса и объекта являются ключевыми в объектно-ориентированных языках программирования, к которым относится также и язык Python. В нем всё является объектами – и строки, и списки, и словари, и функции, и даже сами классы.

Класс (от англ. class) – это шаблон кода, который используется для описания структуры и создания объектов, т.е. экземпляров этого класса.

По сути классы в Python представляют собой типы данных, а объекты – отдельные экземпляры этих типов. Например, в инструкциях a = 'abcdef' и b = 'ABCDEF' переменным a и b присваиваются объекты строкового типа данных, т.е. экземпляры встроенного класса string.

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

Благодаря использованию классов Python обладает всеми преимуществами абстрактного подхода в программировании. В частности Питону присущи:

  • Полиморфизм (от англ. polymorphism) – способность функций и методов обрабатывать данные разных типов. Простыми примерами полиморфизма могут служить: встроенная функция len(s), которая может работать с разными типами встроенных последовательностей, возвращая количество их элементов; метод s.count(x), который подсчитывает количество символов x в строке s (один тип данных) или, например, количество элементов x в списке s (другой тип данных); операторы сложения и умножения, которые для чисел (один тип данных) производят операции сложения и умножения, а для строк (другой тип данных) конкатенацию и повторение конкатенации указанное число раз.
  • Инкапсуляция (от англ. encapsulation) – механизм, который позволяет объединять данные и методы, работающие с этими данными, в единый объект, скрывая при этом детали реализации от пользователя. Грамотно написанный класс должен ограничивать доступность своих членов и взаимодействовать с пользователем только с помощью своего интерфейса. При этом нужно помнить, что в Python инкапсуляция работает лишь на уровне соглашения между программистами о том, какие атрибуты следует считать общедоступными, а какие – приватными (подробнее об этом мы поговорим чуть ниже).
  • Наследование (от англ. inheritance) – еще одна концепция объектно-ориентированного программирования, которая позволяет на основе одного суперкласса создавать множественные подклассы, заимствующие его данные и функциональность с возможностью их изменения и добавления своих данных и функциональности. При этом в Python поддерживается возможность множественного наследования, когда подкласс наследует атрибуты сразу нескольких суперклассов одновременно. Таким образом, наследование способствует повторному использованию уже написанных компонентов программы, помогая избежать избыточности исходного кода.
  • Композиция (агрегирование) (от англ. composition) – возможность создания классов, включающих в себя вызовы уже существующих классов. В результате при создании объектов такого класса они будут состоять или содержать объекты других классов. Здесь важно не путать композицию с наследованием, в том числе множественным. Ведь при наследовании подкласс получает все атрибуты (т.е. возможности) своих суперклассов, а при композиции класс-агрегатор атрибуты не наследует, он просто создает объекты этих классов для использования, например, в качестве своих атрибутов или локальных переменных своих методов.

Благодаря всем этим преимуществам ООП у программистов появляется возможность параллельной разработки отдельных независимых модулей в виде классов, способных скрывать от внешнего мира детали своего внутреннего устройства и объединяющихся в масштабные комплексные приложения посредством предназначенных для этого интерфейсов. Данный процесс можно наглядно представить на примере из жизни, когда отдельные модули МКС собираются независимо друг от друга разными странами, а затем на орбите собираются в одну монолитную космическую станцию за счет имеющихся интерфейсов, обеспе­чивающих взаимодействие готовых модулей между собой.

Для создания классов в Python используется инструкция class, которая в общем виде может быть представлена в следующем формате:

class ClassName(SuperclassName_1, …, SuperclassName_n):
'''Документация класса ClassName.__doc__'''
# Атрибут данных класса (доступен всем экземплярам).
class_data = class_value
# Конструктор класса (инициализация экземпляра).
def __init__(self, init_arg_1, init_arg_2, …, init_arg_n):
# Атрибут данных конкретного экземпляра.
self.init_obj_data = init_obj_value
# Атрибут-метод (доступен всем экземплярам).
def class_method(self, arg_1, arg_2, …, arg_n):
# Атрибут данных конкретного экземпляра.
self.obj_data = obj_value

В заголовке инструкции сперва записывается служебное слово class, затем через пробел указывается имя класса, которое по принятому в Python соглашению обычно записывается в нотации CupWords, далее в скобках перечисляются имена наследуемых классов (их еще называют суперклассами) и завершается заголовок двоеточием.

Если создаваемый класс не наследует других классов, заголовок инструкции разрешается записывать в формате class ClassName:, опуская круглые скобки и записывая двоеточие сразу после имени класса.

В любом случае после заголовка записывается вложенный блок инструкций, в котором перечисляются атрибуты данных класса (по умолчанию они доступны для использования всеми создаваемыми экземплярами класса) и атрибуты-методы для обработки его данных, представляющие собой обычные функции, определенные внутри класса и принимающие в качестве первого аргумента параметр self, которому при вызове методов интерпретатор автоматически присваивает объект текущего экземпляра класса. При необходимости в начале тела класса разрешается указывать строку документирования класса, а также конструктор класса __init__(), представляющий собой специальный метод класса, который вызывается автоматически всякий раз при создании нового экземпляра, инициализируя его исходными данными. Изменение имени конструктора запрещено.

После того, как класс будет определен, можно приступать к созданию его отдельных экземпляров, сохраняя их в переменных или структурах данных. Для этого нужно после имени класса просто указать круглые скобки, перечислив в них начальные данные для инициализации создаваемого экземпляра (см. пример №1).

Python. Урок 14. Классы и объекты

Данный урок посвящен объектно-ориентированному программированию в Python. Разобраны такие темы как создание объектов и классов, работа с конструктором, наследование и полиморфизм в Python.

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

Выделяют три основных “столпа” ООП- это инкапсуляция, наследование и полиморфизм.

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

Под инкапсуляцией понимается сокрытие деталей реализации, данных и т.п. от внешней стороны. Например, можно определить класс “холодильник”, который будет содержать следующие данные: производитель, объем, количество камер хранения, потребляемая мощность и т.п.

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

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

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

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

Примером базового класса, демонстрирующего наследование, можно определить класс “автомобиль”, имеющий атрибуты: масса, мощность двигателя, объем топливного бака и методы: завести и заглушить.

У такого класса может быть потомок – “грузовой автомобиль”, он будет содержать те же атрибуты и методы, что и класс “автомобиль”, и дополнительные свойства: количество осей, мощность компрессора и т.

п..

Полиморфизм

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

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

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

Создание классов и объектов

Создание класса в Python начинается с инструкции class. Вот так будет выглядеть минимальный класс.

class C:
pass

  • Класс состоит из объявления (инструкция class), имени класса (нашем случае это имя C) и тела класса, которое содержит атрибуты и методы (в нашем минимальном классе есть только одна инструкция pass).
  • Для того чтобы создать объект класса необходимо воспользоваться следующим синтаксисом:
  • имя_объекта = имя_класса()

Статические и динамические атрибуты класса

Как уже было сказано выше, класс может содержать атрибуты и методы. Атрибут может быть статическим и динамическим (уровня объекта класса). Суть в том, что для работы со статическим атрибутом, вам не нужно создавать экземпляр класса, а для работы с динамическим – нужно. Пример:

class Rectangle:
    default_color = «green»

    def __init__(self, width, height):
        self.width = width
        self.height = height

В представленном выше классе, атрибут default_color – это статический атрибут, и доступ к нему, как было сказано выше, можно получить не создавая объект класса Rectangle.

Читайте также:  Python + MySQL: как подключиться к СУБД MySQL и работать с ней с помощью Python

>>> Rectangle.default_color
'green'

width и height – это динамические атрибуты, при их создании было использовано ключевое слово self. Пока просто примите это как должное, более подробно про self будет рассказано ниже. Для доступа к width и height предварительно нужно создать объект класса Rectangle:

>>> rect = Rectangle(10, 20)
>>> rect.width
10
>>> rect.height
20

Если обратиться через класс, то получим ошибку:

>>> Rectangle.width
Traceback (most recent call last):
  File «», line 1, in
AttributeError: type object 'Rectangle' has no attribute 'width'

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

Проверим ещё раз значение атрибута default_color:

>>> Rectangle.default_color
'green'

Присвоим ему новое значение:

>>> Rectangle.default_color = «red»
>>> Rectangle.default_color
'red'

Создадим два объекта класса Rectangle и проверим, что default_color у них совпадает:

>>> r1 = Rectangle(1,2)
>>> r2 = Rectangle(10, 20)
>>> r1.default_color
'red'
>>> r2.default_color
'red'

Если поменять значение default_color через имя класса Rectangle, то все будет ожидаемо: у объектов r1 и r2 это значение изменится, но если поменять его через экземпляр класса, то у экземпляра будет создан атрибут с таким же именем как статический, а доступ к последнему будет потерян:

Меняем default_color через r1:

>>> r1.default_color = «blue»
>>> r1.default_color
'blue'

При этом у r2 остается значение статического атрибута:

>>> r2.default_color
'red'
>>> Rectangle.default_color
'red'

Вообще напрямую работать с атрибутами – не очень хорошая идея, лучше для этого использовать свойства.

Методы класса

Добавим к нашему классу метод. Метод – это функция, находящаяся внутри класса и выполняющая определенную работу.

Методы бывают статическими, классовыми (среднее между статическими и обычными) и уровня класса (будем их называть просто словом метод). Статический метод создается с декоратором @staticmethod, классовый – с декоратором @classmethod, первым аргументом в него передается cls, обычный метод создается без специального декоратора, ему первым аргументом передается self:

class MyClass:

    @staticmethod
    def ex_static_method():
        print(«static method»)

    @classmethod
    def ex_class_method(cls):
        print(«class method»)

    def ex_method(self):
        print(«method»)

Статический и классовый метод можно вызвать, не создавая экземпляр класса, для вызова ex_method() нужен объект:

>>> MyClass.ex_static_method()
static method

>>> MyClass.ex_class_method()
class method

>>> MyClass.ex_method()
Traceback (most recent call last):
  File «», line 1, in
TypeError: ex_method() missing 1 required positional argument: 'self'

>>> m = MyClass()
>>> m.ex_method()
method

Конструктор класса и инициализация экземпляра класса

В Python разделяют конструктор класса и метод для инициализации экземпляра класса. Конструктор класса это метод __new__(cls, *args, **kwargs) для инициализации экземпляра класса используется метод __init__(self).

При этом, как вы могли заметить __new__ – это классовый метод, а __init__ таким не является. Метод __new__ редко переопределяется, чаще используется реализация от базового класса object (см.

раздел Наследование), __init__ же наоборот является очень удобным способом задать параметры объекта при его создании.

Создадим реализацию класса Rectangle с измененным конструктором и инициализатором, через который задается ширина и высота прямоугольника:

class Rectangle:

    def __new__(cls, *args, **kwargs):
        print(«Hello from __new__»)
        return super().__new__(cls)

    def __init__(self, width, height):
        print(«Hello from __init__»)
        self.width = width
        self.height = height

>>> rect = Rectangle(10, 20)
Hello from __new__
Hello from __init__

>>> rect.width
10

>>> rect.height
20

Что такое self?

До этого момента вы уже успели познакомиться с ключевым словом self. self – это ссылка на текущий экземпляр класса, в таких языках как Java, C# аналогом является ключевое слово this. Через self вы получаете доступ к атрибутам и методам класса внутри него:

class Rectangle:

    def __init__(self, width, height):
        self.width = width
        self.height = height

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

В приведенной реализации метод area получает доступ к атрибутам width и height для расчета площади. Если бы в качестве первого параметра не было указано self, то при попытке вызвать area программа была бы остановлена с ошибкой.

Уровни доступа атрибута и метода

Если вы знакомы с языками программирования Java, C#, C++ то, наверное, уже задались вопросом: “а как управлять уровнем доступа?”. В перечисленных языка вы можете явно указать для переменной, что доступ к ней снаружи класса запрещен, это делается с помощью ключевых слов (private, protected и т.д.).

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

Хорошим тоном считается, что для чтения/изменения какого-то атрибута должны использоваться специальные методы, которые называются getter/setter, их можно реализовать, но ничего не помешает изменить атрибут напрямую.

При этом есть соглашение, что метод или атрибут, который начинается с нижнего подчеркивания, является скрытым, и снаружи класса трогать его не нужно (хотя сделать это можно).

Внесем соответствующие изменения в класс Rectangle:

class Rectangle:

    def __init__(self, width, height):
        self._width = width
        self._height = height

    def get_width(self):
        return self._width

    def set_width(self, w):
        self._width = w

    def get_height(self):
        return self._height

    def set_height(self, h):
        self._height = h

    def area(self):
        return self._width * self._height

В приведенном примере для доступа к _width и _height используются специальные методы, но ничего не мешает вам обратиться к ним (атрибутам) напрямую.

>>> rect = Rectangle(10, 20)

>>> rect.get_width()
10

>>> rect._width
10

Если же атрибут или метод начинается с двух подчеркиваний, то тут напрямую вы к нему уже не обратитесь (простым образом). Модифицируем наш класс Rectangle:

class Rectangle:

    def __init__(self, width, height):
        self.__width = width
        self.__height = height

    def get_width(self):
        return self.__width

    def set_width(self, w):
        self.__width = w

    def get_height(self):
        return self.__height

    def set_height(self, h):
        self.__height = h

    def area(self):
        return self.__width * self.__height

Попытка обратиться к __width напрямую вызовет ошибку, нужно работать только через get_width():

>>> rect = Rectangle(10, 20)

>>> rect.__width
Traceback (most recent call last):
  File «», line 1, in
AttributeError: 'Rectangle' object has no attribute '__width'

>>> rect.get_width()
10

Но на самом деле это сделать можно, просто этот атрибут теперь для внешнего использования носит название: _Rectangle__width:

>>> rect._Rectangle__width
10

>>> rect._Rectangle__width = 20

>>> rect.get_width()
20

Свойства

Свойством называется такой метод класса, работа с которым подобна работе с атрибутом. Для объявления метода свойством необходимо использовать декоратор @property.

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

Сделаем реализацию класса Rectangle с использованием свойств:

class Rectangle:

    def __init__(self, width, height):
        self.__width = width
        self.__height = height

    @property
    def width(self):
        return self.__width

    @width.setter
    def width(self, w):
        if w > 0:
            self.__width = w
        else:
            raise ValueError

    @property
    def height(self):
        return self.__height

    @height.setter
    def height(self, h):
        if h > 0:
            self.__height = h
        else:
            raise ValueError

    def area(self):
        return self.__width * self.__height

Теперь работать с width и height можно так, как будто они являются атрибутами:

>>> rect = Rectangle(10, 20)

>>> rect.width
10

>>> rect.height
20

Можно не только читать, но и задавать новые значения свойствам:

>>> rect.width = 50

>>> rect.width
50

>>> rect.height = 70

>>> rect.height
70

Если вы обратили внимание: в setter’ах этих свойств осуществляется проверка входных значений, если значение меньше нуля, то будет выброшено исключение ValueError:

>>> rect.width = -10
Traceback (most recent call last):
  File «», line 1, in
  File «test.py», line 28, in width
    raise ValueError
ValueError

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

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

Не все языки программирования поддерживают множественное наследование, но в Python можно его использовать. По умолчанию все классы в Python являются наследниками от object, явно этот факт указывать не нужно.

  1. Синтаксически создание класса с указанием его родителя выглядит так:
  2. class имя_класса(имя_родителя1, [имя_родителя2,…, имя_родителя_n])
  3. Переработаем наш пример так, чтобы в нем присутствовало наследование:

class Figure:

    def __init__(self, color):
        self.__color = color

    @property
    def color(self):
        return self.__color

    @color.setter
    def color(self, c):
        self.__color = c

class Rectangle(Figure): 

    def __init__(self, width, height, color):
        super().__init__(color)
        self.__width = width
        self.__height = height

    @property
    def width(self):
        return self.__width

    @width.setter
    def width(self, w):
        if w > 0:
            self.__width = w
        else:
            raise ValueError

    @property
    def height(self):
        return self.__height

    @height.setter
    def height(self, h):
        if h > 0:
            self.__height = h
       else:
            raise ValueError 

    def area(self):
        return self.__width * self.__height

Родительским классом является Figure, который при инициализации принимает цвет фигуры и предоставляет его через свойства. Rectangle – класс наследник от Figure. Обратите внимание на его метод __init__: в нем первым делом вызывается конструктор (хотя это не совсем верно, но будем говорить так) его родительского класса:

  • super().__init__(color)
  • super – это ключевое слово, которое используется для обращения к родительскому классу.
  • Теперь у объекта класса Rectangle помимо уже знакомых свойств width и height появилось свойство color:

>>> rect = Rectangle(10, 20, «green»)

>>> rect.width
10

>>> rect.height
20

>>> rect.color
'green'

>>> rect.color = «red»

>>> rect.color
'red'

Полиморфизм

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

class Figure:

   def __init__(self, color):
        self.__color = color

    @property
    def color(self):
        return self.__color

    @color.setter
    def color(self, c):
        self.__color = c

    def info(self):
       print(«Figure»)
       print(«Color: » + self.__color)

class Rectangle(Figure):

    def __init__(self, width, height, color):
        super().__init__(color)
        self.__width = width
        self.__height = height

    @property
    def width(self):
        return self.__width

    @width.setter
    def width(self, w):
        if w > 0:
            self.__width = w
        else:
            raise ValueError

    @property
    def height(self):
        return self.__height

    @height.setter
    def height(self, h):
        if h > 0:
            self.__height = h
        else:
            raise ValueError

    def info(self):
        print(«Rectangle»)
        print(«Color: » + self.color)
        print(«Width: » + str(self.width))
        print(«Height: » + str(self.height))
        print(«Area: » + str(self.area()))

    def area(self):
        return self.__width * self.__height

Посмотрим, как это работает

>>> fig = Figure(«orange»)

>>> fig.info()
Figure
Color: orange

>>> rect = Rectangle(10, 20, «green»)

>>> rect.info()
Rectangle
Color: green
Width: 10
Height: 20
Area: 200

Таким образом, класс наследник может расширять функционал класса родителя.

P.S.

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

>

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *