Что такое инкапсуляция в python
Перейти к содержимому

Что такое инкапсуляция в python

  • автор:

Инкапсуляция, наследование, полиморфизм

Python 3 логотип

Недавно мы говорили об основах объектно-ориентированного программирования в 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 

Задачи¶

  1. Описать класс десятичного счётчика. Он должен обладать внутренней переменной, хранящей текущее значение, методами повышения значения ( increment ) и понижения ( decrement ), получения текущего значения get_counter . Учесть, что счётчик не может опускаться ниже 0.
  2. Создать классы для травоядного животного и травы. Животное должно уметь поедать траву, если испытывает голод, в противном случае отказываться от лакомства. Трава должна обладать питательностью, в зависимости от которой животное будет насыщаться.
  3. Создать класс для часов. Должна быть возможность установить время при создании объекта. Также необходимо реализовать методы, с помощью которых можно добавлять по одной минуте/секунде или по одному часу к текущему времени. Помнить, что значения минут и секунд не могут превышать 59, а часов 23.
  4. Доработать предыдущую задачу, чтобы можно было складывать двое часов друг с другом. Для перегрузки оператора + использовать метод __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 .

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

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