В Python существует три основных уровня доступа к атрибутам класса: публичный, защищённый и приватный. Посмотрим на них в одном блоке для наглядности:
attr
_attr
__attr
Пример с использованием всех трёх видов атрибутов:
class Example:
def __init__(self):
self.attr = "Публичный атрибут" # public
self._attr = "Защищённый атрибут" # protected
self.__attr = "Приватный атрибут" # private
def display(self):
print(f"Публичный: {self.attr}")
print(f"Защищённый: {self._attr}")
print(f"Приватный: {self.__attr}")
example = Example()
example.display()
# Попробуем получить доступ к атрибутам извне класса
print(example.attr) # Публичный атрибут доступен
print(example._attr) # Защищённый атрибут доступен, но так делать не рекомендуется
# print(example.__attr) # Приватный атрибут вызовет ошибку AttributeError
Результат:
Публичный: Публичный атрибут
Защищённый: Защищённый атрибут
Приватный: Приватный атрибут
Публичный атрибут
Защищённый атрибут
Попытка получить доступ к приватному атрибуту напрямую вызовет ошибку AttributeError
, показывая, что этот атрибут действительно скрыт от внешнего мира.
Теперь перейдём к более подробному разбору каждого уровня доступа.
По умолчанию все атрибуты и методы в Python являются публичными. Это означает, что они доступны в любом месте программы, как внутри класса, так и вне его. Рассмотрим пример с публичными атрибутами:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def display_info(self):
print(f"Имя: {self.name}, Возраст: {self.age}")
person = Person("Алексей", 32)
person.display_info()
# Мы можем получить доступ к атрибутам напрямую
print(person.name)
print(person.age)
Результат:
Имя: Алексей, Возраст: 32
Алексей
32
В данном примере доступ к атрибутам и методам объекта класса осуществляется напрямую. Однако в реальных приложениях открытый доступ ко всем данным может быть нежелателен, особенно если нужно защитить чувствительную информацию.
Защищённые атрибуты и методы принято обозначать одним подчеркиванием (_
) в начале их имени. Это является сигналом для разработчиков, что данные не предназначены для внешнего использования, хотя Python технически не запрещает их доступ извне. Это больше соглашение, чем реальная защита:
class Employee:
def __init__(self, name, salary):
self.name = name
self._salary = salary # защищённый атрибут
def display_salary(self):
print(f"Зарплата сотрудника: {self._salary}")
employee = Employee("Иван", 50000)
employee.display_salary()
# Мы всё ещё можем получить доступ к защищённому атрибуту
print(employee._salary)
Результат:
Зарплата сотрудника: 50000
50000
Как видно, мы можем обращаться к атрибуту _salary
вне класса, но префикс подчеркивания сигнализирует, что так делать не следует. Это соглашение помогает избежать случайного использования защищённых данных.
Для строгого ограничения доступа используется приватный режим. Приватные атрибуты и методы начинаются с двух подчеркиваний (__
). Такие атрибуты недоступны напрямую извне класса и могут быть использованы только внутри самого класса.
class BankAccount:
def __init__(self, owner, balance):
self.__owner = owner # приватный атрибут
self.__balance = balance # приватный атрибут
def show_balance(self):
print(f"Владелец: {self.__owner}, Баланс: {self.__balance}")
def __secret_method(self):
print("Это приватный метод")
account = BankAccount("Олег", 100000)
account.show_balance()
# Попытка прямого доступа к приватным атрибутам вызовет ошибку
# print(account.__owner) # AttributeError
# print(account.__balance) # AttributeError
# account.__secret_method() # AttributeError
Результат:
Владелец: Олег, Баланс: 100000
Здесь мы видим, что напрямую получить доступ к атрибутам __owner
и __balance
, а также вызвать метод __secret_method
нельзя. Это защищает приватные данные класса от внешнего вмешательства.
Приватные атрибуты можно сделать доступными через специальные методы, например, для чтения данных:
class BankAccount:
def __init__(self, owner, balance):
self.__owner = owner
self.__balance = balance
def show_balance(self):
print(f"Баланс: {self.__balance}")
def get_owner(self):
return self.__owner
account = BankAccount("Сергей", 200000)
print(f"Владелец счёта: {account.get_owner()}")
Результат:
Владелец счёта: Сергей
В данном примере метод get_owner
позволяет безопасно получить информацию о владельце счета, не раскрывая сам атрибут напрямую.
Несмотря на приватность, в Python всё ещё можно получить доступ к приватным данным с помощью специального синтаксиса. Это называется "name mangling" (искажение имени). Имена приватных атрибутов и методов преобразуются Python во внутреннюю форму, к которой можно обратиться:
class BankAccount:
def __init__(self, owner, balance):
self.__owner = owner
self.__balance = balance
account = BankAccount("Иван", 300000)
print(account._BankAccount__owner) # Обход приватности
Результат:
Иван
Таким образом, используя внутреннее имя, мы всё-таки можем получить доступ к приватным атрибутам. Однако это считается плохой практикой и противоречит принципам инкапсуляции.
Попробуем вызвать приватный метод через открытый метод внутри класса:
class BankAccount:
def __init__(self, owner, balance):
self.__owner = owner
self.__balance = balance
def show_info(self):
print(f"Владелец: {self.__owner}")
self.__private_method()
def __private_method(self):
print("Это приватный метод, вызванный из публичного метода")
account = BankAccount("Марина", 400000)
account.show_info()
# Попытка вызвать приватный метод напрямую
# account.__private_method() # AttributeError
Результат:
Владелец: Марина
Это приватный метод, вызванный из публичного метода
Приватные методы можно использовать внутри класса, но они остаются недоступны напрямую извне.
Вот таблица по лекции о уровнях доступа к атрибутам класса в Python:
Уровень доступа | Описание | Пример | Доступ извне |
---|---|---|---|
Публичный | Доступен отовсюду. | self.attr | Доступен: example.attr |
Защищённый | Рекомендуется использовать только внутри класса и подклассов. | self._attr | Доступен, но не рекомендуется: example._attr |
Приватный | Доступен только внутри самого класса. | self.__attr | Недоступен: example.__attr вызовет ошибку |
В Python существует три уровня доступа: