Перейти к основному содержимому

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

Классы

protected атрибуты

Атрибут класса с одним префиксным подчеркиванием говорит, что параметр используется только внутри класса. При этом он доступен для обращения извне. Это ограничение доступа только на уровне соглашения.

class Foo:
def __init__(self):
self._protected = 1337


print(Foo()._protected) # 1337

private атрибуты

Атрибут класса с двумя префиксными подчеркиваниями доступен внутри класса, но недоступен извне.

class Foo:
def __init__(self):
self.__private = 1337

print(Foo().__private) # AttributeError: 'Foo' object has no attribute '__private'

Name mangling

Интерпретатор назначает таким атрибутам имя вида _ClassName__fieldName. Этот прием называется name mangling. Поэтому атрибут оказывается не таким уж недоступным:

print(Foo()._Foo__private)  # 1337

Наследование

Посмотрим, как ведут себя protected и private атрибуты при наследовании:

class Foo:
def __init__(self):
self._protected = 1
self.__private = 2


class Bar(Foo):
pass


foo = Foo()
print(f"{foo._protected = }")
print(f"{foo._Foo__private = }")

bar = Bar()
print(f"{bar._protected = }")
print(f"{bar._Bar__private = }")
foo._protected = 1
foo._Foo__private = 2
bar._protected = 1
AttributeError: 'Bar' object has no attribute '_Bar__private'. Did you mean: '_Foo__private'?

Как мы видим, с _protected атрибутом все хорошо. А вот из-за name mangling Bar унаследовал от Foo атрибут __private под именем _Foo__private.

Модули

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

moda.py:

_protected = 1
__private = 2

Обе переменные можно напрямую импортировать:

modb.py:

from moda import _protected, __private

print(_protected) # 1
print(__private) # 2

Однако если импортировать с помощью звездочки, то мы получим ошибку:

from moda import *

print(_protected, __private) # NameError: name '_protected' is not defined

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

moda.py:

_protected = 1
__private = 2

__all__ = ['_protected', '__private']

modc.py:

from moda import *

print(_protected) # 1
print(__private) # 2

На прямой импорт это никак не повлияет. Допустим, мы добавили в __all__ только __private:

moda.py:

_protected = 1
__private = 2

__all__ = ['__private']

Тогда мы все еще будем иметь доступ к _protected, если импортировать его напрямую:

modb.py:

from moda import _protected, __private

print(_protected) # 1
print(__private) # 2