Инкапсуляция, наследование, полиморфизм
Недавно мы говорили об основах объектно-ориентированного программирования в python, теперь продолжим эту тему и поговорим о таких понятиях ООП, как инкапсуляция, наследование и полиморфизм.
Инкапсуляция
Инкапсуляция — ограничение доступа к составляющим объект компонентам (методам и переменным). Инкапсуляция делает некоторые из компонент доступными только внутри класса.
Инкапсуляция в Python работает лишь на уровне соглашения между программистами о том, какие атрибуты являются общедоступными, а какие — внутренними.
Одиночное подчеркивание в начале имени атрибута говорит о том, что переменная или метод не предназначен для использования вне методов класса, однако атрибут доступен по этому имени.
Двойное подчеркивание в начале имени атрибута даёт большую защиту: атрибут становится недоступным по этому имени.
File
Однако полностью это не защищает, так как атрибут всё равно остаётся доступным под именем _ИмяКласса__ИмяАтрибута:
Наследование
Наследование подразумевает то, что дочерний класс содержит все атрибуты родительского класса, при этом некоторые из них могут быть переопределены или добавлены в дочернем. Например, мы можем создать свой класс, похожий на словарь:
Для вставки кода на Python в комментарий заключайте его в теги
- Модуль csv - чтение и запись CSV файлов
- Создаём сайт на Django, используя хорошие практики. Часть 1: создаём проект
- Онлайн-обучение Python: сравнение популярных программ
- Книги о Python
- GUI (графический интерфейс пользователя)
- Курсы Python
- Модули
- Новости мира Python
- NumPy
- Обработка данных
- Основы программирования
- Примеры программ
- Типы данных в Python
- Видео
- Python для Web
- Работа для Python-программистов
- Сделай свой вклад в развитие сайта!
- Самоучитель Python
- Карта сайта
- Отзывы на книги по Python
- Реклама на сайте
Инкапсуляция, наследование, полиморфизм
Инкапсуляция — ограничение доступа к составляющим объект компонентам (методам и переменным). Инкапсуляция делает некоторые из компонент доступными только внутри класса.
Инкапсуляция в Python работает лишь на уровне соглашения между программистами о том, какие атрибуты являются общедоступными, а какие — внутренними.
Одиночное подчеркивание в начале имени атрибута говорит о том, что переменная или метод не предназначен для использования вне методов класса, однако атрибут доступен по этому имени.
class A: def _private(self): print("Это приватный метод!") >>> a = A() >>> a._private() Это приватный метод!
Двойное подчеркивание в начале имени атрибута даёт большую защиту: атрибут становится недоступным по этому имени.
>>> class B: . def __private(self): . print("Это приватный метод!") . >>> b = B() >>> b.__private() Traceback (most recent call last): File "", line 1, in AttributeError: 'B' object has no attribute '__private'
Однако полностью это не защищает, так как атрибут всё равно остаётся доступным под именем _ИмяКласса__ИмяАтрибута:
>>> b._B__private() Это приватный метод!
Наследование
Наследование подразумевает то, что дочерний класс содержит все атрибуты родительского класса, при этом некоторые из них могут быть переопределены или добавлены в дочернем. Например, мы можем создать свой класс, похожий на словарь:
>>> class Mydict(dict): . def get(self, key, default = 0): . return dict.get(self, key, default) . >>> a = dict(a=1, b=2) >>> b = Mydict(a=1, b=2)
Класс Mydict ведёт себя точно так же, как и словарь, за исключением того, что метод get по умолчанию возвращает не None, а 0.
>>> b['c'] = 4 >>> print(b) >>> print(a.get('v')) None >>> print(b.get('v')) 0
Полиморфизм
Полиморфизм - разное поведение одного и того же метода в разных классах. Например, мы можем сложить два числа, и можем сложить две строки. При этом получим разный результат, так как числа и строки являются разными классами.
>>> 1 + 1 2 >>> "1" + "1" '11'
Инкапсуляция¶
В объектно-ориентированных языках программирования есть спецификаторы, ограничивающие доступ к аттрибутам. Главной задачей является отделение деталей реализации логики объекта от того как с ним надо взаимодействовать.
В языке Python в отличие от многих других языков вместо ключевых слов используется знак нижнего подчёркивания.
public - Данный аттрибут доступен всем:
class Person: def __init__(self, name): self.name = name # публичный аттрибут p = Person("John") p.name = "Mark"
private - Аттрибут сокрыт от посторонних глаз и доступен только самому объекту:
class Person: def __init__(self, name, age): self.name = name self.set_age(age) def get_age(self): return self.__age def set_age(self, age): if age >= 0: self.__age = int(age) # __age - приватный аттрибут else: self.__age = 0 print("Возраст не может быть отрицательным!") p = Person("John", 19) print(p.get_age()) # 19 p.set_age(20) print(p.get_age()) # 20 p.__age = 21 # Error p._Person__age = 21 print(p.get_age()) # 21
Задачи¶
- Описать класс десятичного счётчика. Он должен обладать внутренней переменной, хранящей текущее значение, методами повышения значения ( increment ) и понижения ( decrement ), получения текущего значения get_counter . Учесть, что счётчик не может опускаться ниже 0.
- Создать классы для травоядного животного и травы. Животное должно уметь поедать траву, если испытывает голод, в противном случае отказываться от лакомства. Трава должна обладать питательностью, в зависимости от которой животное будет насыщаться.
- Создать класс для часов. Должна быть возможность установить время при создании объекта. Также необходимо реализовать методы, с помощью которых можно добавлять по одной минуте/секунде или по одному часу к текущему времени. Помнить, что значения минут и секунд не могут превышать 59, а часов 23.
- Доработать предыдущую задачу, чтобы можно было складывать двое часов друг с другом. Для перегрузки оператора + использовать метод __add__(self, other) .
© Copyright Revision d00c0df4 .
Built with Sphinx using a theme provided by Read the Docs.
Read the Docs v: latest
Versions latest Downloads html On Read the Docs Project Home Builds Free document hosting provided by Read the Docs.
Что такое инкапсуляция в python
По умолчанию атрибуты в классах являются общедоступными, а это значит, что из любого места программы мы можем получить атрибут объекта и изменить его. Например:
class Person: def __init__(self, name): self.name = name # устанавливаем имя self.age = 1 # устанавливаем возраст def display_info(self): print(f"Имя: \tВозраст: ") tom = Person("Tom") tom.name = "Человек-паук" # изменяем атрибут name tom.age = -129 # изменяем атрибут age tom.display_info() # Имя: Человек-паук Возраст: -129
Но в данном случае мы можем, к примеру, присвоить возрасту или имени человека некорректное значение, например, указать отрицательный возраст. Подобное поведение нежелательно, поэтому встает вопрос о контроле за доступом к атрибутам объекта.
С данной проблемой тесно связано понятие инкапсуляции. Инкапсуляция является фундаментальной концепцией объектно-ориентированного программирования. Она предотвращает прямой доступ к атрибутам объект из вызывающего кода.
Касательно инкапсуляции непосредственно в языке программирования Python скрыть атрибуты класса можно сделав их приватными или закрытыми и ограничив доступ к ним через специальные методы, которые еще называются свойствами .
Изменим выше определенный класс, определив в нем свойства:
class Person: def __init__(self, name): self.__name = name # устанавливаем имя self.__age = 1 # устанавливаем возраст def set_age(self, age): if 1 < age < 110: self.__age = age else: print("Недопустимый возраст") def get_age(self): return self.__age def get_name(self): return self.__name def display_info(self): print(f"Имя: \tВозраст: ") tom = Person("Tom") tom.display_info() # Имя: Tom Возраст: 1 tom.set_age(-3486) # Недопустимый возраст tom.set_age(25) tom.display_info() # Имя: Tom Возраст: 25
Для создания приватного атрибута в начале его наименования ставится двойной прочерк: self.__name . К такому атрибуту мы сможем обратиться только из того же класса. Но не сможем обратиться вне этого класса. Например, присвоение значения этому атрибуту ничего не даст:
tom.__age = 43
Потому что в данном случае просто определяется динамически новый атрибут __age, но это он не имеет ничего общего с атрибутом self.__age .
А попытка получить его значение приведет к ошибке выполнения (если ранее не была определена переменная __age):
print(tom.__age)
Однако все же нам может потребоваться устанавливать возраст пользователя из вне. Для этого создаются свойства. Используя одно свойство, мы можем получить значение атрибута:
def get_age(self): return self.__age
Данный метод еще часто называют геттер или аксессор.
Для изменения возраста определено другое свойство:
def set_age(self, age): if 1 < age < 110: self.__age = age else: print("Недопустимый возраст")
Данный метод еще называют сеттер или мьютейтор (mutator). Здесь мы уже можем решить в зависимости от условий, надо ли переустанавливать возраст.
Необязательно создавать для каждого приватного атрибута подобную пару свойств. Так, в примере выше имя человека мы можем установить только из конструктора. А для получение определен метод get_name.
Аннотации свойств
Выше мы рассмотрели, как создавать свойства. Но Python имеет также еще один - более элегантный способ определения свойств. Этот способ предполагает использование аннотаций, которые предваряются символом @.
Для создания свойства-геттера над свойством ставится аннотация @property .
Для создания свойства-сеттера над свойством устанавливается аннотация имя_свойства_геттера.setter .
Перепишем класс Person с использованием аннотаций:
class Person: def __init__(self, name): self.__name = name # устанавливаем имя self.__age = 1 # устанавливаем возраст @property def age(self): return self.__age @age.setter def age(self, age): if 1 < age < 110: self.__age = age else: print("Недопустимый возраст") @property def name(self): return self.__name def display_info(self): print(f"Имя: \tВозраст: ") tom = Person("Tom") tom.display_info() # Имя: Tom Возраст: 1 tom.age = -3486 # Недопустимый возраст print(tom.age) # 1 tom.age = 36 tom.display_info() # Имя: Tom Возраст: 36
Во-первых, стоит обратить внимание, что свойство-сеттер определяется после свойства-геттера.
Во-вторых, и сеттер, и геттер называются одинаково - age. И поскольку геттер называется age, то над сеттером устанавливается аннотация @age.setter .
После этого, что к геттеру, что к сеттеру, мы обращаемся через выражение tom.age .