Инкапсуляция в Python
Цель инкапсуляции — защитить данные внутри класса.
Класс хранит свои данные (свойства) и методы в одном месте и решает, как код вне класса может получить доступ к этим данным.
В результате данные сложнее изменить случайно, а внутреннее устройство класса остаётся скрытым.
Приватные свойства
Начните имя свойства с двойного подчёркивания __, и Python будет считать его приватным:
Примечание: код вне класса не имеет прямого доступа к приватному свойству.
Пример
Класс с приватным свойством __age:
Вывод: полная программа на Python
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age # Приватное свойство
p1 = Person("Emil", 25)
print(p1.name)
print(p1.__age) # Это вызовет ошибку
Получение значения приватного свойства
Метод-геттер даёт внешнему коду возможность прочитать приватное свойство:
Пример
Прочитайте приватное свойство через метод-геттер:
Вывод: полная программа на Python
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age
def get_age(self):
return self.__age
p1 = Person("Tobias", 25)
print(p1.get_age())
Изменение значения приватного свойства
Изменение приватного свойства работает так же — через метод-сеттер.
Метод-сеттер также может проверить новое значение, прежде чем его сохранить:
Пример
Измените приватное свойство через метод-сеттер:
Вывод: полная программа на Python
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age
def get_age(self):
return self.__age
def set_age(self, age):
if age > 0:
self.__age = age
else:
print("Возраст должен быть положительным")
p1 = Person("Tobias", 25)
print(p1.get_age())
p1.set_age(26)
print(p1.get_age())
Зачем нужна инкапсуляция?
Инкапсуляция даёт несколько преимуществ:
- Защита данных: данные защищены от случайных изменений
- Проверка: новые значения можно проверить, прежде чем они будут сохранены
- Гибкость: внутренний код может меняться, а код вне класса продолжает работать
- Контроль: только вы решаете, как данные читаются и изменяются
Пример
Защитите значение и проверьте его с помощью инкапсуляции:
Вывод: полная программа на Python
class Student:
def __init__(self, name):
self.name = name
self.__grade = 0
def set_grade(self, grade):
if 0 <= grade <= 100:
self.__grade = grade
else:
print("Оценка должна быть от 0 до 100")
def get_grade(self):
return self.__grade
def get_status(self):
if self.__grade >= 60:
return "Сдано"
else:
return "Не сдано"
student = Student("Emil")
student.set_grade(85)
print(student.get_grade())
print(student.get_status())
Защищённые свойства
Для защищённых свойств в Python есть соглашение: начинать имя с одного подчёркивания _:
Примечание: это лишь соглашение, и Python не требует его соблюдения. _ просто сообщает другим программистам, что свойство предназначено для внутреннего использования.
Пример
Класс с защищённым свойством:
Вывод: полная программа на Python
class Person:
def __init__(self, name, salary):
self.name = name
self._salary = salary # Защищённое свойство
p1 = Person("Linus", 50000)
print(p1.name)
print(p1._salary) # Обратиться можно, но не следует
Приватные методы
Префикс из двойного подчёркивания работает и с методами, делая их приватными:
Примечание: приватный метод ведёт себя как приватное свойство: внешний код не может вызвать его напрямую. Здесь __validate доступен только другим методам класса.
Пример
Класс с приватным методом:
Вывод: полная программа на Python
class Calculator:
def __init__(self):
self.result = 0
def __validate(self, num):
if not isinstance(num, (int, float)):
return False
return True
def add(self, num):
if self.__validate(num):
self.result += num
else:
print("Некорректное число")
calc = Calculator()
calc.add(10)
calc.add(5)
print(calc.result)
# calc.__validate(5) # Это вызвало бы ошибку
Искажение имён (name mangling)
Искажение имён (name mangling) — это техника, с помощью которой Python делает свойства и методы приватными.
Имя, которое начинается с двойного подчёркивания __, незаметно изменяется: Python добавляет перед ним _ClassName.
Так, __age превращается в _Person__age.
Примечание: искажённое имя действительно даёт доступ к приватному свойству (его можно использовать), но этого следует избегать, потому что это сводит на нет смысл инкапсуляции.
Пример
Посмотрите на искажённое имя в действии:
Вывод: полная программа на Python
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age
p1 = Person("Emil", 30)
# Так Python искажает имя:
print(p1._Person__age) # Не рекомендуется!