Как стать автором
Обновить

Инкапсуляция в Python 3

Время на прочтение5 мин
Количество просмотров72K

image


Определение


Значение термина «инкапсуляция» расплывчато и отличается от источника к источнику. Принято считать, что инкапсуляция — один из основополагающих принципов ООП, хотя некоторые научные статьи вовсе упускают инкапсуляцию из списка. К примеру, Джон Митчелл в книге «Концепты в языках программирования» при перечислении основных концептов в ООП упоминает только абстракцию — термин который принято считать близким к инкапсуляции по значению, но все-же более обширным и высокоуровневым. С другой стороны, Роберт Мартин в его книге «Чистая архитектура» явно говорит о том, что инкапсуляция, наследование и полиморфизм считается фундаментом ООП.


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


  • связь данных с методами которые этими данными управляют;
  • набор инструментов для управления доступом к данным или методам которые управляют этими данными.

Инкапсуляция как связь


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


#!/usr/bin/python3

class Phone:
    number = "111-11-11"
    def print_number(self):
        print( "Phone number is: ", self.number )

my_phone = Phone()
my_phone.print_number()

input( "Press Enter to exit" )

Класс “Phone” объединяет данные в переменной “number” с методом “print_number()”


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


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


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


  • публичными (public) — данные доступны всем;
  • приватными (private) — данные доступны только объекту/классу которому они принадлежат.

Большинство языков имеют дополнительные степени доступа, которые находятся между этими границами. К примеру, в C++ и Python3 есть три уровня доступа: публичный, защищенный и приватный; C# добавляет ключевое слово «внутренний» (internal) в список.


Стоит отметить, что в большинстве языков программирования, уровень доступа к любым данным установлен по умолчанию. Например, в C++ по умолчанию уровень доступа к данным в классе задан как приватный— к его данным могут обращаться только члены и друзья класса. Стандартный уровень доступа к структуре (struct) в C++ отличается — он публичный, и данные в такой структуре могут быть доступны любому. Уровень доступа для переменных и методов класса в Python 3 полностью зависит от синтаксиса.


Примеры


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


Python 3 предоставляет 3 уровня доступа к данным:


  • публичный (public, нет особого синтаксиса, publicBanana);
  • защищенный (protected, одно нижнее подчеркивание в начале названия, _protectedBanana);
  • приватный (private, два нижних подчеркивания в начала названия, __privateBanana).

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


#!/usr/bin/python3

class Phone:
    username = "Kate"                # public variable
    __how_many_times_turned_on = 0   # private variable

    def call(self):                  # public method
        print( "Ring-ring!" )

    def __turn_on(self):             # private method
        self.__how_many_times_turned_on += 1 
        print( "Times was turned on: ", self.__how_many_times_turned_on )

my_phone = Phone()

my_phone.call()
print( "The username is ", my_phone.username )
# my_phone.turn_on()
# my_phone.__turn_on()
# print( “Turned on: “, my_phone.__how_many_times_turned_on)
# print( “Turned on: “, my_phone.how_many_times_turned_on)
# will produce an error
input( "Press Enter to exit" )

Доступ к публичным переменным и методам можно получить из основной программы. Попытка получить приватные данные или запустить приватный метод приведет к ошибке.


Нарушение инкапсуляции


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


#!/usr/bin/python3

class Phone:
    username = "Kate"                # public variable
    __serial_number = "11.22.33"     # private variable
    __how_many_times_turned_on = 0   # private variable

    def call(self):                  # public method
        print( "Ring-ring!" )

    def __turn_on(self):             # private method
        self.__how_many_times_turned_on += 1 
        print( "Times was turned on: ", self.__how_many_times_turned_on )

my_phone = Phone()

my_phone._Phone__turn_on()
my_phone._Phone__serial_number = "44.55.66"
print( "New serial number is ", my_phone._Phone__serial_number )

input( "Press Enter to exit" )

Несколько слов о Магии


Существуют методы, так называемые «магические методы» («magic methods») или «специальные методы» («special methods»), которые позволяют классам определять свое поведение в отношении стандартных языковых операторов. Примером таких языковых операторов могут служить следующие выражения:


  • x > y
  • x[ i ]

Python 3 поддерживает множество таких методов, полный список можно найти на странице официальной документации языка. __init__ (инициализатор) является наиболее часто используемым из них и запускается при создании нового объекта класса. Другой, __lt__ (расширенное сравнение), определяет правила для сравнения двух объектов пользовательского класса. Такие методы не попадают в категорию «приватных» или «публичных», поскольку служат другим целям и корнями глубоко уходят во внутреннюю структуру языка.


#!/usr/bin/python3

class Phone:
    def __init__(self, number):      # magic method / inititalizer
        print( "The Phone object was created" )
        self.number = number

    def __lt__(self, other):         # magic method / rich comparison
        return self.number < other.number

my_phone = Phone(20)
other_phone = Phone(30)

if my_phone < other_phone:
    print( "Two instances of custom class were compared" )
    print( "'__lt__' was called implicitly" )

if my_phone.__lt__(other_phone):
    print( "Now, '__lt__' was used explicitly" )

input( "Press Enter to exit" )

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


Заключение


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


Источники


  1. John C. Mitchell, Concepts in programming languages
  2. Robert C. Martin, Clean Architecture, A Craftsman’s Guide to Software Structure and Design
Теги:
Хабы:
+11
Комментарии10

Публикации

Истории

Работа

Data Scientist
63 вакансии
Python разработчик
142 вакансии

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн