ООП в Python – это вам не Java
Никаких вам фабрик абстрактных классов, интерфейсов и вот этого всего. Есть, конечно, ABC и очумелые ручки, но это совсем другая история. Конечно, есть классы и наследование. Несмотря на это, в Python нет строгого разделения на public/private/protected.
На фоне этой правды жизни возникло соглашение о том, что одно нижнее подчеркивание (_) перед именем означает пометку «только для внутреннего пользования» и даже в случае from M import * такие имена не будут импортироваться.
Но это всё же лишь соглашение. И если очень хочется, то ничто особо не мешает получить, например, доступ к полю, начинающемуся с нижнего подчеркивания. Самое близкое, в некотором смысле, к private-полю в Python можно получить, написав не одно, а два нижних подчеркивания перед именем.
При попытке прямого доступа в таком случае получается ошибка, например:
Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: CachedResponse instance has no attribute ‘__intermediate_result’
Но даже такой механизм обеспечения приватности не особо сильнее предыдущего, так как на самом деле, значение поля из примера выше можно получить, обратившись к атрибуту _CachedResponse__intermediate_result.
Это так называемый name mangling. Но, в основном, он всё же нужен для того, чтобы избегать инцидентов с одинаковыми именами полей в дочерних классах и т.п.
Есть вопрос? Напишите в комментариях!