Наследование — это важная концепция объектно-ориентированного программирования, которая позволяет одним классам наследовать функциональность, свойства и методы других классов. Но нужно понимать, как отдельные методы и свойства становятся доступными в процессе наследования.
В этом случае помогают публичные, приватные и защищенные методы и свойства, которые позволяют управлять уровнем доступа к определенным аспектам класса. В этом уроке разберемся с этим в деталях.
Python не предоставляет никакой системы строгой инкапсуляции, которая присуща некоторым языкам программирования. Из-за этого вы можете получить доступ к любому атрибуту или методу класса из внешнего кода. Однако существует общепринятое соглашение между разработчиками, что атрибуты и методы, предназначенные для внутреннего использования, должны начинаться с подчеркивания (например, _visible). Это подчеркивание служит индикатором для других разработчиков, что этот атрибут или метод не должен изменяться напрямую извне. Но всегда следует помнить, что Python не предоставляет никаких средств для принудительного соблюдения этого соглашения, и атрибуты с подчеркиванием по-прежнему доступны извне.
Публичные атрибуты и методы
Свойства видимости объектов оказывают влияние не только на внешнее поведение объектов, но и на взаимодействие между классами, которые наследуют их. Публичные свойства и методы доступны всем наследникам и создаются простым определением их в теле класса без каких-либо особых префиксов. К ним можно обращаться как внутри объекта, так и снаружи.
В следующем примере мы создаем два класса: HTMLElement и DivElement. DivElement является потомком HTMLElement и наследует его атрибуты и методы:
Здесь DivElement имеет доступ как к атрибуту visible, так и к методу is_visible(), унаследованным от HTMLElement.
Количество классов в цепочке наследования не влияет на это поведение. Любой подкласс DivElement тоже получит доступ к публичным частям HTMLElement:
В данном примере мы видим, что DivElementWithEmptyBody наследуется от DivElement и имеет доступ к атрибутам и методам классов DivElement и HTMLElement.
Наследование не влияет на поведение свойств внутри объектов. Значение visible в каждом конкретном объекте связано только с этим объектом:
Хотя публичные атрибуты и методы имеют свои преимущества, например, доступность из любого места кода, они также могут представлять определенные риски. К ним можно обращаться и изменять их напрямую из любого класса, что может привести к неожиданным последствиям. Поэтому в некоторых случаях предпочтительно использовать приватные атрибуты и методы.
Приватные атрибуты и методы
Свойства и методы с приватным модификатором доступа доступны только внутри того класса, где они были определены. Приватные свойства и методы в Python создаются путем добавления двойного подчеркивания (__) перед именем атрибута или метода.
Наследники не могут получить доступ к приватным свойствам и методам. Подразумевается, что приватные сущности — это нечто персональное для класса — его внутренняя реализация, которую нельзя выставлять наружу. Но это не отменяет возможности взаимодействовать с приватными данными через публичный интерфейс:
Здесь прямой доступ к приватному атрибуту __visible вызывает ошибку. При этом мы все еще можем получить доступ к значению этого атрибута через публичный метод is_visible().
Приватный модификатор доступа представляет собой мощный механизм инкапсуляции, который позволяет скрыть детали реализации класса. Это помогает сохранить целостность данных и избежать нежелательных изменений во внутреннем состоянии объекта.
Но что делать, когда нам нужен компромисс между полной открытостью публичных и строгим ограничением приватных атрибутов и методов? Для данной ситуации существуют защищенные атрибуты и методы.
Защищенные атрибуты и методы
Защищенные атрибуты и методы имеют необычное поведение. Это смесь между публичными и приватными.
Они создаются путем добавления одного подчеркивания (_) перед именем атрибута или метода. Также они используются, когда разработчик хочет запретить доступ снаружи объекта, но дать возможность работать с ними внутри объекта класса-наследника или суперкласса.
Посмотрим, как это работает на практике:
Защищенные атрибуты и методы в Python рассматриваются как внутренние части класса, и, хотя они могут быть доступны извне, это считается плохой практикой. Вместо этого следует придерживаться соглашения и не обращаться к ним напрямую.
Name Mangling
В Python, в отличие от некоторых других языков, можно определять в наследниках методы совпадающие именами с приватными методами родителя. Но как тогда избежать перезаписи родительского метода? Для этого в языке незаметно от программиста используется механизм name mangling. К названию приватного метода добавляется также название его класса. Так, приватные методы в родителях на самом деле начинаются с имени родительского класса, а в наследниках - с имени наследника.
Таким образом мы можем "защитить" родительские методы от изменения в наследниках. Но стоит помнить, что в Python по-настоящему все доступно для разработчика. И добраться до оригинального родительского метода всегда можно обойдя name mangling как _ИмяКласса__имя_метода.
Разумеется, несмотря на то, что мы знаем как добраться до "оригинала" обходить name mangling крайне опасно, ведь если родительский метод был приватным, значит были причины сделать его таким и неизменяемым в наследниках.
Выбор способа
Мы только начали знакомиться с наследованием, но уже сейчас видно, что всё не просто. Одну и ту же задачу можно сделать множеством способов. Какой предпочесть?
Универсальная стратегия, которой стоит придерживаться в большинстве случаев, – всегда работать через абстракцию, пока она не мешает. Это значит, что все свойства, которые не предназначены для прямого доступа, делаются с префиксом подчеркивания (соглашение о защищенности), а наружу выставляется публичный интерфейс (методы).
Выводы
Чтобы правильно использовать наследование в объектно-ориентированном программировании, необходимо понимать публичные, приватные и защищенные атрибуты и методы. Эти модификаторы доступа позволяют управлять уровнем доступа к функциональности и данным класса, обеспечивая инкапсуляцию и полиморфизм. В итоге это способствует созданию гибкого и надежного кода.
<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 22:56:35 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="lFRU_dGmplLq08i7w6iFlj41KJBCUz9q9oh5I6JdSTh7hZ_KI9gLMlyQ7CPPp3Xh_jwFOkpkwchLaON38FquVg";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Модификаторы доступа | Python: Погружаясь в классы</title>
<meta name="description" content="Модификаторы доступа / Python: Погружаясь в классы: Разберемся, как модификаторы доступа влияют на наследование">
<link rel="canonical" href="https://ru.hexlet.io/courses/python-classes/lessons/visibility/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Модификаторы доступа">
<meta property="og:title" content="Python: Погружаясь в классы">
<meta property="og:description" content="Модификаторы доступа / Python: Погружаясь в классы: Разберемся, как модификаторы доступа влияют на наследование">
<meta property="og:url" content="https://ru.hexlet.io/courses/python-classes/lessons/visibility/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="3PH7F8fYIXOiH3Kuinj3Oc-QX2oQ21uyZrhOwDpfb3czIDAgNaaMExRcVjaGdwdOD5lywBjspRDbWNSUaFiIGQ" />
<script src="/vite/assets/inertia-DfXos102.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-cb8xch9l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczMSwicHVyIjoiYmxvYl9pZCJ9fQ==--f5df4883f3f678321cb4fa96e9ce657bd5ee1adf/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Static%20website-cuate.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc0OSwicHVyIjoiYmxvYl9pZCJ9fQ==--846349326718432328cf5c0677091aca67f80af3/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-amico%20(1).png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T22:56:35.315Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"rFQeHokR2lJAMLNdt3fGTQQEMGY3mB6jxOmCwvw8k4JDhdUpe293MvZzl8W7eDY6xA0dzD-v4AF5CRiWrjt07A","topics":[{"id":93704,"title":"Немного не понял почему написано, что нельзя вызвать приватный метод снаружи класса, хотя это не так\nВот пример\n```python\nclass Foo:\n def __init__(self):\n self.__baz = 42\n\n def foo(self):\n print(self.__baz)\n\n\nx = Foo()\n\nx.foo() # 42\nprint(x._Foo__baz) # 42\n```\n\nЯ понимаю, что в обычной ситуации никто так делать не будет, но странно, что этого вообще не упомянули","plain_title":"Немного не понял почему написано, что нельзя вызвать приватный метод снаружи класса, хотя это не так Вот пример ```python class Foo: def init(self): self.__baz = 42 def foo(self): print(self.__baz) x = Foo() x.foo() # 42 print(x.Foo_baz) # 42 ``` Я понимаю, что в обычной ситуации никто так делать не будет, но странно, что этого вообще не упомянули ","creator":{"public_name":"Сергей","id":542717,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Mamtsev","id":294764,"is_tutor":true},"id":183823,"body":"Ну вы тут схитрили конечно. Вызвать метод это `x.baz`. Но да, про name mangling надо бы добавить","topic_id":93704}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Модификаторы доступа","entity_url":null,"active":true}},{"id":91509,"title":"Эрм, а где задания(упражнения) ...?\nпросто почитать я и в интернете могу...\nнадеюсь практика появится, а пока не буду это проходить какой смысл без закрепления на практике","plain_title":"Эрм, а где задания(упражнения) ...? просто почитать я и в интернете могу... надеюсь практика появится, а пока не буду это проходить какой смысл без закрепления на практике ","creator":{"public_name":"Сергей Науменко","id":567984,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":180960,"body":"Приветствую, Сергей! Курс только появился, со временем мы наполним его квизами и практиками","topic_id":91509}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Модификаторы доступа","entity_url":null,"active":true}},{"id":91706,"title":"Доброго дня (:\n\nИсправьте, пожалуйста, ошибку, допущенную в теории (раздел \"Защищенные атрибуты и методы\"):\n\nВ блоке кода есть метод __init __ (у которого, кстати, пропущен второй андерскор в начале), в котором объявляется не защищенный атрибут \"_visible\", а приватный \"__visible\", из-за этого пример валится с ошибками, ибо в остальных методах вызывается несуществующий \" self._visible\".\n\nПлюс ко всему, защищенный атрибут (с одним андерскором), доступен для извлечения вне класса и из классов-потомков, например такой код не вызовет ошибок:\n```\nclass First:\n def __init__(self):\n self._test = 'test!'\n\n\nclass Second(First):\n def make_test(self):\n return f'this is {self._test}'\n\n\ntest1 = Second()\nprint(test1.make_test()) # this is test!\nprint(test1._test) # test!\n```\n\nИбо защищенные атрибуты не влияют на доступность, а предостерегают разработчика от их использования исключительно своим внешним видом. Ну и IDE желтым этот блок подсвечивает, как бы намекая, что так делать не стоит.\n\nЕсли я не права - исправьте меня, пожалуйста :)","plain_title":"Доброго дня (: Исправьте, пожалуйста, ошибку, допущенную в теории (раздел \"Защищенные атрибуты и методы\"): В блоке кода есть метод init __ (у которого, кстати, пропущен второй андерскор в начале), в котором объявляется не защищенный атрибут \"_visible\", а приватный \"visible\", из-за этого пример валится с ошибками, ибо в остальных методах вызывается несуществующий \" self._visible\". Плюс ко всему, защищенный атрибут (с одним андерскором), доступен для извлечения вне класса и из классов-потомков, например такой код не вызовет ошибок: ``` class First: def init(self): self._test = 'test!' class Second(First): def maketest(self): return f'this is {self.test}' test1 = Second() print(test1.maketest()) # this is test! print(test1.test) # test! ``` Ибо защищенные атрибуты не влияют на доступность, а предостерегают разработчика от их использования исключительно своим внешним видом. Ну и IDE желтым этот блок подсвечивает, как бы намекая, что так делать не стоит. Если я не права - исправьте меня, пожалуйста :) ","creator":{"public_name":"Любовь Никитина","id":486167,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Mamtsev","id":294764,"is_tutor":true},"id":181370,"body":"Спасибо за замечание, перепроверю пример в теории и поправлю.","topic_id":91706}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Модификаторы доступа","entity_url":null,"active":true}},{"id":92444,"title":"в решении учителя используется dict.fromkeys и (в другом месте) генератор списка. Чем хороши эти конструкции для этого задания? В сравнении с простым if и простыми методами списков?","plain_title":"в решении учителя используется dict.fromkeys и (в другом месте) генератор списка. Чем хороши эти конструкции для этого задания? В сравнении с простым if и простыми методами списков? ","creator":{"public_name":"Vadim Kovach","id":239101,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Mamtsev","id":294764,"is_tutor":true},"id":182234,"body":"Разницы нет. Просто разные способы сделать одно и то же.","topic_id":92444}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Модификаторы доступа","entity_url":null,"active":true}},{"id":93436,"title":"Задание не очень понятно сформулировано.\nЧто значит сохранять порядок тэгов? Когда я удаляю тэг а потом, добавляю обратно - он должен вернуться в тоже место или в конец?","plain_title":"Задание не очень понятно сформулировано. Что значит сохранять порядок тэгов? Когда я удаляю тэг а потом, добавляю обратно - он должен вернуться в тоже место или в конец? ","creator":{"public_name":"Алексей","id":461955,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Mamtsev","id":294764,"is_tutor":true},"id":183526,"body":"В конец. Посмотрите на примеры в задании","topic_id":93436},{"creator":{"public_name":"Алексей","id":461955,"is_tutor":false},"id":183546,"body":"Там нету примера, где удаляется не последний тэг и потом добавляется обратно.","topic_id":93436}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Модификаторы доступа","entity_url":null,"active":true}},{"id":95102,"title":"Так инициализировать self.attributes некорректно, потому что при None в self.attributes все равно будет None, а не пустой словарь\n\n```\nclass HTMLElement:\n def __init__(self, attributes=None):\n if attributes is None:\n self.attributes = {}\n self.attributes = attributes\n\ndiv = HTMLElement()\nprint(div.attributes)\n```","plain_title":"Так инициализировать self.attributes некорректно, потому что при None в self.attributes все равно будет None, а не пустой словарь class HTMLElement: def __init__(self, attributes=None): if attributes is None: self.attributes = {} self.attributes = attributes div = HTMLElement() print(div.attributes) ","creator":{"public_name":"","id":394160,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Mamtsev","id":294764,"is_tutor":true},"id":185909,"body":"Спасибо, поправил опечатку","topic_id":95102}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Модификаторы доступа","entity_url":null,"active":true}},{"id":95406,"title":"Привет\n\nВ уроке про позднее связывание написано так:\n\nПредположим, что мы создаем объект класса HTMLAnchorElement, который наследуется от HTMLElement. Каким будет self внутри методов родительского класса? Правильный ответ: HTMLAnchorElement, то есть того класса, объект которого мы создаем\n\nНо в этом уроке в разделе Name mangling приведен такой пример\n\n```\nclass User:\n def __init__(self):\n self.name = \"User\"\n\n # зададим приватный метод\n def __private_greet(self):\n return f\"Hello, {self.name}!\"\n\n # зададим публичный интерфейс для этого метода\n def greet(self):\n return self.__private_greet()\n\nclass John(User):\n # попробуем переопределить приватный метод родителя\n def __private_greet(self):\n return f\"Hello, John!\"\n\n# метод не изменился\nJohn().greet() # 'Hello, User!'\n\n# посмотрим весь список методов John с помощью функции dir\n# здесь мы видим, что к имени приватных методов для защиты от перезаписи добавляется их класс\n# так в наследнике есть оригинальный метод и метод наследника\ndir(John) # ['_John__private_greet', '_User__private_greet', ..]\n```\n\nМы ведь вызываем метод greet у класса John разве в greet не должен был попасть self от John, после чего на основе имени этого класса у нас бы вызвался метод _John__private_greet и вывестись должно было Hello, John ? \n\nИ еще, из следующего урока из части \"Понимание позднего связывания\" не понятна вот эта часть, я немного добавил от себя проверенной в REPL информации, и пока что понять почему работает именно так у меня не получается:\n\n```\nclass A:\n __name = 'From A'\n\n def get_name(self):\n print('Current class:', type(self).__name__)\n print(type(self).__name)\n return self.__name\n\nclass B(A):\n __name = 'From B'\n\na = A()\nprint(a.get_name())\n# => Current class: A\n# => From A\n# => From A\n\nb = B()\nprint(b.get_name())\n# => Current class: B\n# => From A \n# Хотя мы вызываем метод именно у класса В. \n# По сути объект b должен был быть вызван вот так b._B__name, \n# но почему-то все равно выводит From A\n\n# А теперь вызовем напрямую у объекта b метод _B__name \nprint(b._B__name)\n# => From B \n# Теперь почему-то вывелось From B\n```","plain_title":"Привет В уроке про позднее связывание написано так: Предположим, что мы создаем объект класса HTMLAnchorElement, который наследуется от HTMLElement. Каким будет self внутри методов родительского класса? Правильный ответ: HTMLAnchorElement, то есть того класса, объект которого мы создаем Но в этом уроке в разделе Name mangling приведен такой пример class User: def __init__(self): self.name = \"User\" # зададим приватный метод def __private_greet(self): return f\"Hello, {self.name}!\" # зададим публичный интерфейс для этого метода def greet(self): return self.__private_greet() class John(User): # попробуем переопределить приватный метод родителя def __private_greet(self): return f\"Hello, John!\" # метод не изменился John().greet() # 'Hello, User!' # посмотрим весь список методов John с помощью функции dir # здесь мы видим, что к имени приватных методов для защиты от перезаписи добавляется их класс # так в наследнике есть оригинальный метод и метод наследника dir(John) # ['_John__private_greet', '_User__private_greet', ..] Мы ведь вызываем метод greet у класса John разве в greet не должен был попасть self от John, после чего на основе имени этого класса у нас бы вызвался метод Johnprivategreet и вывестись должно было Hello, John ? И еще, из следующего урока из части \"Понимание позднего связывания\" не понятна вот эта часть, я немного добавил от себя проверенной в REPL информации, и пока что понять почему работает именно так у меня не получается: class A: __name = 'From A' def get_name(self): print('Current class:', type(self).__name__) print(type(self).__name) return self.__name class B(A): __name = 'From B' a = A() print(a.get_name()) # => Current class: A # => From A # => From A b = B() print(b.get_name()) # => Current class: B # => From A # Хотя мы вызываем метод именно у класса В. # По сути объект b должен был быть вызван вот так b._B__name, # но почему-то все равно выводит From A # А теперь вызовем напрямую у объекта b метод _B__name print(b._B__name) # => From B # Теперь почему-то вывелось From B ","creator":{"public_name":"Муса Алаев","id":401733,"is_tutor":false},"comments":[{"creator":{"public_name":"Муса Алаев","id":401733,"is_tutor":false},"id":186197,"body":"Все, теперь понял как работают эти вызовы, спасибо","topic_id":95406},{"creator":{"public_name":"Ivan Mamtsev","id":294764,"is_tutor":true},"id":186186,"body":"1.\n> Мы ведь вызываем метод greet у класса John разве в greet не должен был попасть self от John, после чего на основе имени этого класса у нас бы вызвался метод _John__private_greet и вывестись должно было Hello, John ?\n\nНет, потому что когда создается приватный метод, то к его названию добавляется имя класса. Вы можете предстваить, что написано так:\n\n```python\nclass User:\n def __init__(self):\n self.name = \"User\"\n \n def _User__private_greet(self):\n return f\"Hello, {self.name}!\"\n \n def greet(self):\n return self._User__private_greet()\n```\n\n2.\n> И еще, из следующего урока из части \"Понимание позднего связывания\" не понятна вот эта часть, я немного добавил от себя проверенной в REPL информации, и пока что понять почему работает именно так у меня не получается:\n\nЗдесь то же самое. Представьте что перед атрибутом добавлено имя класса, в котором он объявлен\n\n```python\nclass A:\n _A__name = 'From A'\n \n def get_name(self):\n print('Current class:', type(self).__name__)\n print(type(self)._A__name)\n return self_A.__name\n \nclass B(A):\n _B__name = 'From B\n```","topic_id":95406}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Модификаторы доступа","entity_url":null,"active":true}},{"id":99411,"title":"Здравствуйте! Почему выбраны именно такие модификаторы доступа? Я решил приватный доступ установить и все прошло. \nhttps://ru.hexlet.io/code_reviews/1407665 ","plain_title":"Здравствуйте! Почему выбраны именно такие модификаторы доступа? Я решил приватный доступ установить и все прошло. https://ru.hexlet.io/code_reviews/1407665 ","creator":{"public_name":"Рамис Сабирзянов","id":597312,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Mamtsev","id":294764,"is_tutor":true},"id":190888,"body":"Здесь достаточно только приватного доступа, то есть просто обозначить для других разработчиков, что это внутренние методы, и их не нужно менять или выносить наружу.","topic_id":99411}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Модификаторы доступа","entity_url":null,"active":true}},{"id":98849,"title":"не совсем понимаю, почему в учительском решении объединяются два списка, на основе результата создается словарь, который преобразуется в список. Неужели это на столько эффективнее простого \n```python\nif a not in b:\n b.append(a)\n``` \nчто бы пренебрегать \"Simple is better than complex\"","plain_title":"не совсем понимаю, почему в учительском решении объединяются два списка, на основе результата создается словарь, который преобразуется в список. Неужели это на столько эффективнее простого python if a not in b: b.append(a) что бы пренебрегать \"Simple is better than complex\" ","creator":{"public_name":"Владислав Смирнов","id":656520,"is_tutor":false},"comments":[{"creator":{"public_name":"Ivan Mamtsev","id":294764,"is_tutor":true},"id":190245,"body":"В случае объединения словарей мы сразу отбрасываем повторящиеся ключи. Но ваша запись тоже корректная.","topic_id":98849}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Модификаторы доступа","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":2694,"slug":"python_classes_visibility_exercise","name":null,"state":"active","kind":"exercise","language":"python","locale":"ru","has_web_view":false,"has_test_view":false,"reviewable":true,"readme":"## HTMLElement.py\n\nРеализуйте набор методов для работы с тегами:\n\n* `add_tag(tag_name)` – добавляет тег\n* `remove_tag(tag_name)` – удаляет тег\n* `toggle_tag(tag_name)` – ставит тег если его не было и убирает если он был\n\nЭти методы должны обрабатывать свойство `'tag'` (внутри строка) словаря `attributes`. В процессе реализации вам понадобится постоянно преобразовывать строку тегов в список и обратно. Вынесите эту операцию в отдельные функции и установите им правильный модификатор доступа.\n\n```python\ndiv = HTMLDivElement({'tag': 'one two'})\ndiv.get_attribute('tag') ## 'one two'\n\ndiv.add_tag('small')\ndiv.get_attribute('tag') ## 'one two small'\n\ndiv.add_tag('small')\ndiv.get_attribute('tag') ## 'one two small'\n\ndiv.remove_tag('two')\ndiv.get_attribute('tag') ## 'one small'\n\ndiv.toggle_tag('small')\ndiv.get_attribute('tag') ## 'one'\n\ndiv.toggle_tag('small')\ndiv.get_attribute('tag') ## 'one small'\n```\n\n### Подсказки\n\n* Для фильтрации уникальных тегов и сохранения порядка их в списке вам может понадобиться `dict.fromkeys`\n","prepared_readme":"## HTMLElement.py\n\nРеализуйте набор методов для работы с тегами:\n\n* `add_tag(tag_name)` – добавляет тег\n* `remove_tag(tag_name)` – удаляет тег\n* `toggle_tag(tag_name)` – ставит тег если его не было и убирает если он был\n\nЭти методы должны обрабатывать свойство `'tag'` (внутри строка) словаря `attributes`. В процессе реализации вам понадобится постоянно преобразовывать строку тегов в список и обратно. Вынесите эту операцию в отдельные функции и установите им правильный модификатор доступа.\n\n```python\ndiv = HTMLDivElement({'tag': 'one two'})\ndiv.get_attribute('tag') ## 'one two'\n\ndiv.add_tag('small')\ndiv.get_attribute('tag') ## 'one two small'\n\ndiv.add_tag('small')\ndiv.get_attribute('tag') ## 'one two small'\n\ndiv.remove_tag('two')\ndiv.get_attribute('tag') ## 'one small'\n\ndiv.toggle_tag('small')\ndiv.get_attribute('tag') ## 'one'\n\ndiv.toggle_tag('small')\ndiv.get_attribute('tag') ## 'one small'\n```\n\n### Подсказки\n\n* Для фильтрации уникальных тегов и сохранения порядка их в списке вам может понадобиться `dict.fromkeys`\n","has_solution":true,"entity_name":"Модификаторы доступа"},"units":[{"id":9446,"name":"theory","url":"/courses/python-classes/lessons/visibility/theory_unit"},{"id":9447,"name":"quiz","url":"/courses/python-classes/lessons/visibility/quiz_unit"},{"id":9613,"name":"exercise","url":"/courses/python-classes/lessons/visibility/exercise_unit"}],"links":[{"id":422812,"name":"Соглашение между разработчиками","url":"https://peps.python.org/pep-0008/#method-names-and-instance-variables"}],"ordered_units":[{"id":9446,"name":"theory","url":"/courses/python-classes/lessons/visibility/theory_unit"},{"id":9447,"name":"quiz","url":"/courses/python-classes/lessons/visibility/quiz_unit"},{"id":9613,"name":"exercise","url":"/courses/python-classes/lessons/visibility/exercise_unit"}],"id":4202,"slug":"visibility","state":"approved","name":"Модификаторы доступа","course_order":120,"goal":"Разберемся, как модификаторы доступа влияют на наследование","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Наследование — это важная концепция объектно-ориентированного программирования, которая позволяет одним классам наследовать функциональность, свойства и методы других классов. Но нужно понимать, как отдельные методы и свойства становятся доступными в процессе наследования.\n\nВ этом случае помогают публичные, приватные и защищенные методы и свойства, которые позволяют управлять уровнем доступа к определенным аспектам класса. В этом уроке разберемся с этим в деталях.\n\n## Модификаторы доступа в Python\n\nPython не предоставляет никакой системы строгой инкапсуляции, которая присуща некоторым языкам программирования. Из-за этого вы можете получить доступ к любому атрибуту или методу класса из внешнего кода. Однако существует общепринятое соглашение между разработчиками, что атрибуты и методы, предназначенные для внутреннего использования, должны начинаться с подчеркивания (например, `_visible`). Это подчеркивание служит индикатором для других разработчиков, что этот атрибут или метод не должен изменяться напрямую извне. Но всегда следует помнить, что Python не предоставляет никаких средств для принудительного соблюдения этого соглашения, и атрибуты с подчеркиванием по-прежнему доступны извне.\n\n## Публичные атрибуты и методы\n\nСвойства видимости объектов оказывают влияние не только на внешнее поведение объектов, но и на взаимодействие между классами, которые наследуют их. Публичные свойства и методы доступны всем наследникам и создаются простым определением их в теле класса без каких-либо особых префиксов. К ним можно обращаться как внутри объекта, так и снаружи.\n\nВ следующем примере мы создаем два класса: `HTMLElement` и `DivElement`. `DivElement` является потомком `HTMLElement` и наследует его атрибуты и методы:\n\n```python\nclass HTMLElement:\n def __init__(self):\n self.visible = True\n\n def is_visible(self):\n return self.visible\n\n\nclass DivElement(HTMLElement):\n def is_visible_property_from_parent(self):\n return self.visible\n\n def is_visible_method_from_parent(self):\n return self.is_visible()\n\n\ndiv = DivElement()\n\n# Вызов родительского свойства напрямую\nprint(div.visible) # => True\n# Вызов родительского метода напрямую\nprint(div.is_visible()) # => True\n\n# Вызов родительского свойства изнутри объекта\nprint(div.is_visible_property_from_parent()) # => True\n\n# Вызов родительского метода изнутри объекта\nprint(div.is_visible_method_from_parent()) # => True\n```\n\nЗдесь `DivElement` имеет доступ как к атрибуту `visible`, так и к методу `is_visible()`, унаследованным от `HTMLElement`.\n\nКоличество классов в цепочке наследования не влияет на это поведение. Любой подкласс `DivElement` тоже получит доступ к публичным частям `HTMLElement`:\n\n```python\nclass DivElementWithEmptyBody(DivElement):\n pass\n\n\ndiv = DivElementWithEmptyBody()\n\n# Вызов родительского свойства напрямую\nprint(div.visible) # => True\n# Вызов родительского метода напрямую\nprint(div.is_visible()) # => True\n\n# Вызов родительского свойства изнутри объекта\nprint(div.is_visible_property_from_parent()) # => True\n# Вызов родительского метода изнутри объекта\nprint(div.is_visible_method_from_parent()) # => True\n```\n\nВ данном примере мы видим, что `DivElementWithEmptyBody` наследуется от `DivElement` и имеет доступ к атрибутам и методам классов `DivElement` и `HTMLElement`.\n\nНаследование не влияет на поведение свойств внутри объектов. Значение `visible` в каждом конкретном объекте связано только с этим объектом:\n\n```python\ndiv1 = DivElementWithEmptyBody()\ndiv2 = DivElement()\n\nprint(div1.visible) ## => True\nprint(div2.visible) ## => True\n\ndiv1.visible = False\nprint(div1.visible) ## => False\nprint(div2.visible) ## => True\n```\n\nХотя публичные атрибуты и методы имеют свои преимущества, например, доступность из любого места кода, они также могут представлять определенные риски. К ним можно обращаться и изменять их напрямую из любого класса, что может привести к неожиданным последствиям. Поэтому в некоторых случаях предпочтительно использовать приватные атрибуты и методы.\n\n## Приватные атрибуты и методы\n\nСвойства и методы с приватным модификатором доступа доступны только внутри того класса, где они были определены. Приватные свойства и методы в Python создаются путем добавления двойного подчеркивания (`__`) перед именем атрибута или метода.\n\nНаследники не могут получить доступ к приватным свойствам и методам. Подразумевается, что приватные сущности — это нечто персональное для класса — его внутренняя реализация, которую нельзя выставлять наружу. Но это не отменяет возможности взаимодействовать с приватными данными через публичный интерфейс:\n\n```python\nclass HTMLElement:\n def __init__(self):\n self.__visible = True\n\n def is_visible(self):\n return self.__visible\n\n\nclass DivElement(HTMLElement):\n pass\n\n\ndiv = DivElement()\n\nprint(div.is_visible()) # => True\nprint(div.__visible)\n# ...\n# AttributeError: 'DivElement' object has no attribute '__visible'\n```\n\nЗдесь прямой доступ к приватному атрибуту `__visible` вызывает ошибку. При этом мы все еще можем получить доступ к значению этого атрибута через публичный метод `is_visible()`.\n\nПриватный модификатор доступа представляет собой мощный механизм инкапсуляции, который позволяет скрыть детали реализации класса. Это помогает сохранить целостность данных и избежать нежелательных изменений во внутреннем состоянии объекта.\n\nНо что делать, когда нам нужен компромисс между полной открытостью публичных и строгим ограничением приватных атрибутов и методов? Для данной ситуации существуют защищенные атрибуты и методы.\n\n## Защищенные атрибуты и методы\n\nЗащищенные атрибуты и методы имеют необычное поведение. Это смесь между публичными и приватными.\n\nОни создаются путем добавления одного подчеркивания (`_`) перед именем атрибута или метода. Также они используются, когда разработчик хочет запретить доступ снаружи объекта, но дать возможность работать с ними внутри объекта класса-наследника или суперкласса.\n\nПосмотрим, как это работает на практике:\n\n```python\nclass HTMLElement:\n def __init__(self):\n self._visible = True\n\n def is_visible(self):\n return self._visible\n\n\nclass DivElement(HTMLElement):\n def isVisiblePropertyFromParent(self):\n return self._visible\n\n\ndiv = DivElement()\n\n# Доступно внутри через родительский метод\nprint(div.is_visible()) # => True\n\n# Доступно внутри напрямую\nprint(div.isVisiblePropertyFromParent()) # => True\n\n# Доступно снаружи, но это нарушает соглашение\nprint(div._visible) # => True\n```\n\nЗащищенные атрибуты и методы в Python рассматриваются как внутренние части класса, и, хотя они могут быть доступны извне, это считается плохой практикой. Вместо этого следует придерживаться соглашения и не обращаться к ним напрямую.\n\n## Name Mangling\n\nВ Python, в отличие от некоторых других языков, можно определять в наследниках методы совпадающие именами с приватными методами родителя. Но как тогда избежать перезаписи родительского метода? Для этого в языке незаметно от программиста используется механизм name mangling. К названию приватного метода добавляется также название его класса. Так, приватные методы в родителях на самом деле начинаются с имени родительского класса, а в наследниках - с имени наследника.\n\n```python\nclass User:\n def __init__(self):\n self.name = \"User\"\n\n # зададим приватный метод\n def __private_greet(self):\n return f\"Hello, {self.name}!\"\n\n # зададим публичный интерфейс для этого метода\n def greet(self):\n return self.__private_greet()\n\n\nclass John(User):\n # попробуем переопределить приватный метод родителя\n def __private_greet(self):\n return f\"Hello, John!\"\n\n\n# метод не изменился\nJohn().greet() # 'Hello, User!'\n\n# посмотрим весь список методов John с помощью функции dir\n# здесь мы видим, что к имени приватных методов для защиты от перезаписи добавляется их класс\n# так в наследнике есть оригинальный метод и метод наследника\ndir(John) # ['_John__private_greet', '_User__private_greet', ..]\n```\n\nТаким образом мы можем \"защитить\" родительские методы от изменения в наследниках. Но стоит помнить, что в Python по-настоящему все доступно для разработчика. И добраться до оригинального родительского метода всегда можно обойдя name mangling как `_ИмяКласса__имя_метода`.\n\n```python\nclass User:\n def __init__(self):\n self.name = \"User\"\n\n # зададим приватный метод\n def __private_greet(self):\n return f\"Hello, {self.name}!\"\n\n # зададим публичный интерфейс для этого метода\n def greet(self):\n return self.__private_greet()\n\n\nclass John(User):\n # попробуем переопределить приватный метод родителя\n # на этот раз доберемся до оригинального метода обойдя name mangling\n def _User__private_greet(self):\n return f\"Hello, John!\"\n\n\n# метод изменился\nJohn().greet() # Hello, John!\n```\n\nРазумеется, несмотря на то, что мы знаем как добраться до \"оригинала\" обходить name mangling крайне опасно, ведь если родительский метод был приватным, значит были причины сделать его таким и неизменяемым в наследниках.\n\n## Выбор способа\n\nМы только начали знакомиться с наследованием, но уже сейчас видно, что всё не просто. Одну и ту же задачу можно сделать множеством способов. Какой предпочесть?\n\nУниверсальная стратегия, которой стоит придерживаться в большинстве случаев, – всегда работать через абстракцию, пока она не мешает. Это значит, что все свойства, которые не предназначены для прямого доступа, делаются с префиксом подчеркивания (соглашение о защищенности), а наружу выставляется публичный интерфейс (методы).\n\n## Выводы\n\nЧтобы правильно использовать наследование в объектно-ориентированном программировании, необходимо понимать публичные, приватные и защищенные атрибуты и методы. Эти модификаторы доступа позволяют управлять уровнем доступа к функциональности и данным класса, обеспечивая инкапсуляцию и полиморфизм. В итоге это способствует созданию гибкого и надежного кода.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":9443,"name":"theory","url":"/courses/python-classes/lessons/intro/theory_unit"}],"links":[],"ordered_units":[{"id":9443,"name":"theory","url":"/courses/python-classes/lessons/intro/theory_unit"}],"id":4200,"slug":"intro","state":"approved","name":"О курсе","course_order":100,"goal":"Узнаем о курсе, его структуре, задачах и целях","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Python, как и другие объектно-ориентированные языки программирования, уделяет большое внимание организации классов. Это необходимо, чтобы упростить повторное использование кода и сократить количество ошибок.\n\nС одной стороны это хорошо. Но с другой — текущих возможностей настолько много, что одну и ту же задачу можно реализовать десятками способов. Многообразие подходов порождает различные школы и направления в написании кода. В итоге возникает много вопросов: Как часто нужно использовать наследование? Где применяются абстрактные классы? А анонимные? Зачем нужны миксины? Как совмещать подтипы и иерархии?\n\nВ этом курсе мы глубоко погрузимся в структуру классов и познакомимся с наследованием. Мы узнаем, как правильно строить иерархии классов, учитывая принцип подстановки Барбары Лисков. Также мы рассмотрим, почему наследование не всегда является лучшим способом организации кода и почему композиция может быть предпочтительнее наследования.\n\nНаследование влечет за собой множество новых концепций. Здесь появляются абстрактные классы, переопределение методов и внутренние классы. Появляются шаблоны проектирования, которые уникальны для наследования. Наследование влияет на то, как работает полиморфный код. Все это требует отдельного рассмотрения.\n\nВ конце курса мы познакомимся с декораторами — уникальным для Python механизмом расширения функциональности, лишенным недостатков наследования.\n"},"id":372,"slug":"python-classes","challenges_count":3,"name":"Python: Погружаясь в классы","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы глубже познакомитесь с классами на Python. Вы узнаете о принципе подстановки Лисков, различных типах методов, а также паттерне \"шаблонный метод\". Поймете, как эффективно писать код, зная особенности ООП внутри Python. Знания из курса пригодятся, чтобы правильно выбирать между наследованием и композицией, а также улучшить ваш код с помощью метаклассов.","kind":"advanced","updated_at":"2026-01-20T11:38:51.827Z","language":"python","duration_cache":54600,"skills":["Грамотно выбирать между наследованием и композицией","Следовать принципу подстановки Лисков при построении иерархий классов","Уменьшать дублирование с помощью трейтов и абстрактных классов","Реализовывать паттерн \"шаблонный метод\"","Эффективно писать код зная как устроено ООП внутри Python"],"keywords":[],"lessons_count":14,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NjAwNywicHVyIjoiYmxvYl9pZCJ9fQ==--d1b51c47377360635cdc5dd72d60bf676f28cd9e/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2ZpbGwiOls2MDAsNDAwXX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--25b4ee73052eb43df9a50f4a4459ce26d57b6a5d/image.png"},"recommendedLandings":[{"stack":{"id":7,"slug":"python","title":"Python-разработчик","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":10,"duration_in_months":10},"id":7,"slug":"python","title":"Python-разработчик ","subtitle":"Изучите Python, Django, REST и Fast API для создания веб-приложений","subtitle_for_lists":"Изучите Python, Django, REST и Fast API для создания веб-приложений","locale":"ru","current":true,"duration_in_months_text":"10 месяцев","stack_slug":"python","price_text":"от 6 792 ₽","duration_text":"10 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczMSwicHVyIjoiYmxvYl9pZCJ9fQ==--f5df4883f3f678321cb4fa96e9ce657bd5ee1adf/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Static%20website-cuate.png"},{"stack":{"id":67,"slug":"python-oop","title":"ООП на Python","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":4350,"duration_in_months":2},"id":120,"slug":"python-oop","title":"ООП на Python","subtitle":"Навык понимания архитектуры и чистого кода, позволяющий проходить собеседования, решать задачи и увеличивать зарплату","subtitle_for_lists":"Изучите архитектуру и чистый код на Python","locale":"ru","current":true,"duration_in_months_text":"2 месяца","stack_slug":"python-oop","price_text":"от 3 900 ₽","duration_text":"2 месяца","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc0OSwicHVyIjoiYmxvYl9pZCJ9fQ==--846349326718432328cf5c0677091aca67f80af3/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-amico%20(1).png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/python-classes/lessons/visibility/theory_unit","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><div style="position:absolute;top:0rem" class=""></div><div style="max-width:var(--container-size-xl);height:100%;min-height:0rem" class=""><style data-mantine-styles="inline">.__m__-_R_5ub_{--grid-gutter:0rem;}</style><div style="height:100%;min-height:0rem" class="m_410352e9 mantine-Grid-root __m__-_R_5ub_"><div class="m_dee7bd2f mantine-Grid-inner" style="height:100%"><style data-mantine-styles="inline">.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:91.66666666666667%;--col-max-width:91.66666666666667%;}@media(min-width: 48em){.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem;display:flex" class="m_96bdd299 mantine-Grid-col __m__-_R_rdub_"><style data-mantine-styles="inline">.__m__-_R_6qrdub_{margin-top:0rem;padding-inline:var(--mantine-spacing-xs);width:100%;}@media(min-width: 48em){.__m__-_R_6qrdub_{margin-top:var(--mantine-spacing-xl);width:80%;}}@media(min-width: 62em){.__m__-_R_6qrdub_{padding-inline:var(--mantine-spacing-xl);}}</style><div style="margin-inline:auto;max-width:var(--mantine-breakpoint-xl)" class="__m__-_R_6qrdub_"><div style="color:var(--mantine-color-dimmed)" class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:calc(0.125rem * var(--mantine-scale));color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-lock "><path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6"></path><path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path><path d="M8 11v-4a4 4 0 1 1 8 0v4"></path></svg></div><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Python: Погружаясь в классы</p></div><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size);margin-bottom:var(--mantine-spacing-xl)" class="m_8a5d1357 mantine-Title-root" data-order="1">Теория: Модификаторы доступа</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"Модификаторы доступа","inLanguage":"ru","isPartOf":{"@type":"LearningResource","name":"Python: Погружаясь в классы"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><p>Наследование — это важная концепция объектно-ориентированного программирования, которая позволяет одним классам наследовать функциональность, свойства и методы других классов. Но нужно понимать, как отдельные методы и свойства становятся доступными в процессе наследования.</p>
<p>В этом случае помогают публичные, приватные и защищенные методы и свойства, которые позволяют управлять уровнем доступа к определенным аспектам класса. В этом уроке разберемся с этим в деталях.</p>
<h2 id="heading-2-1">Модификаторы доступа в Python</h2>
<p>Python не предоставляет никакой системы строгой инкапсуляции, которая присуща некоторым языкам программирования. Из-за этого вы можете получить доступ к любому атрибуту или методу класса из внешнего кода. Однако существует общепринятое соглашение между разработчиками, что атрибуты и методы, предназначенные для внутреннего использования, должны начинаться с подчеркивания (например, <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">_visible</code>). Это подчеркивание служит индикатором для других разработчиков, что этот атрибут или метод не должен изменяться напрямую извне. Но всегда следует помнить, что Python не предоставляет никаких средств для принудительного соблюдения этого соглашения, и атрибуты с подчеркиванием по-прежнему доступны извне.</p>
<h2 id="heading-2-2">Публичные атрибуты и методы</h2>
<p>Свойства видимости объектов оказывают влияние не только на внешнее поведение объектов, но и на взаимодействие между классами, которые наследуют их. Публичные свойства и методы доступны всем наследникам и создаются простым определением их в теле класса без каких-либо особых префиксов. К ним можно обращаться как внутри объекта, так и снаружи.</p>
<p>В следующем примере мы создаем два класса: <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">HTMLElement</code> и <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">DivElement</code>. <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">DivElement</code> является потомком <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">HTMLElement</code> и наследует его атрибуты и методы:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">class HTMLElement:
def __init__(self):
self.visible = True
def is_visible(self):
return self.visible
class DivElement(HTMLElement):
def is_visible_property_from_parent(self):
return self.visible
def is_visible_method_from_parent(self):
return self.is_visible()
div = DivElement()
# Вызов родительского свойства напрямую
print(div.visible) # => True
# Вызов родительского метода напрямую
print(div.is_visible()) # => True
# Вызов родительского свойства изнутри объекта
print(div.is_visible_property_from_parent()) # => True
# Вызов родительского метода изнутри объекта
print(div.is_visible_method_from_parent()) # => True</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Здесь <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">DivElement</code> имеет доступ как к атрибуту <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">visible</code>, так и к методу <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">is_visible()</code>, унаследованным от <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">HTMLElement</code>.</p>
<p>Количество классов в цепочке наследования не влияет на это поведение. Любой подкласс <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">DivElement</code> тоже получит доступ к публичным частям <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">HTMLElement</code>:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">class DivElementWithEmptyBody(DivElement):
pass
div = DivElementWithEmptyBody()
# Вызов родительского свойства напрямую
print(div.visible) # => True
# Вызов родительского метода напрямую
print(div.is_visible()) # => True
# Вызов родительского свойства изнутри объекта
print(div.is_visible_property_from_parent()) # => True
# Вызов родительского метода изнутри объекта
print(div.is_visible_method_from_parent()) # => True</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>В данном примере мы видим, что <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">DivElementWithEmptyBody</code> наследуется от <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">DivElement</code> и имеет доступ к атрибутам и методам классов <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">DivElement</code> и <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">HTMLElement</code>.</p>
<p>Наследование не влияет на поведение свойств внутри объектов. Значение <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">visible</code> в каждом конкретном объекте связано только с этим объектом:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">div1 = DivElementWithEmptyBody()
div2 = DivElement()
print(div1.visible) ## => True
print(div2.visible) ## => True
div1.visible = False
print(div1.visible) ## => False
print(div2.visible) ## => True</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Хотя публичные атрибуты и методы имеют свои преимущества, например, доступность из любого места кода, они также могут представлять определенные риски. К ним можно обращаться и изменять их напрямую из любого класса, что может привести к неожиданным последствиям. Поэтому в некоторых случаях предпочтительно использовать приватные атрибуты и методы.</p>
<h2 id="heading-2-3">Приватные атрибуты и методы</h2>
<p>Свойства и методы с приватным модификатором доступа доступны только внутри того класса, где они были определены. Приватные свойства и методы в Python создаются путем добавления двойного подчеркивания (<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">__</code>) перед именем атрибута или метода.</p>
<p>Наследники не могут получить доступ к приватным свойствам и методам. Подразумевается, что приватные сущности — это нечто персональное для класса — его внутренняя реализация, которую нельзя выставлять наружу. Но это не отменяет возможности взаимодействовать с приватными данными через публичный интерфейс:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">class HTMLElement:
def __init__(self):
self.__visible = True
def is_visible(self):
return self.__visible
class DivElement(HTMLElement):
pass
div = DivElement()
print(div.is_visible()) # => True
print(div.__visible)
# ...
# AttributeError: 'DivElement' object has no attribute '__visible'</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Здесь прямой доступ к приватному атрибуту <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">__visible</code> вызывает ошибку. При этом мы все еще можем получить доступ к значению этого атрибута через публичный метод <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">is_visible()</code>.</p>
<p>Приватный модификатор доступа представляет собой мощный механизм инкапсуляции, который позволяет скрыть детали реализации класса. Это помогает сохранить целостность данных и избежать нежелательных изменений во внутреннем состоянии объекта.</p>
<p>Но что делать, когда нам нужен компромисс между полной открытостью публичных и строгим ограничением приватных атрибутов и методов? Для данной ситуации существуют защищенные атрибуты и методы.</p>
<h2 id="heading-2-4">Защищенные атрибуты и методы</h2>
<p>Защищенные атрибуты и методы имеют необычное поведение. Это смесь между публичными и приватными.</p>
<p>Они создаются путем добавления одного подчеркивания (<code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">_</code>) перед именем атрибута или метода. Также они используются, когда разработчик хочет запретить доступ снаружи объекта, но дать возможность работать с ними внутри объекта класса-наследника или суперкласса.</p>
<p>Посмотрим, как это работает на практике:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">class HTMLElement:
def __init__(self):
self._visible = True
def is_visible(self):
return self._visible
class DivElement(HTMLElement):
def isVisiblePropertyFromParent(self):
return self._visible
div = DivElement()
# Доступно внутри через родительский метод
print(div.is_visible()) # => True
# Доступно внутри напрямую
print(div.isVisiblePropertyFromParent()) # => True
# Доступно снаружи, но это нарушает соглашение
print(div._visible) # => True</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Защищенные атрибуты и методы в Python рассматриваются как внутренние части класса, и, хотя они могут быть доступны извне, это считается плохой практикой. Вместо этого следует придерживаться соглашения и не обращаться к ним напрямую.</p>
<h2 id="heading-2-5">Name Mangling</h2>
<p>В Python, в отличие от некоторых других языков, можно определять в наследниках методы совпадающие именами с приватными методами родителя. Но как тогда избежать перезаписи родительского метода? Для этого в языке незаметно от программиста используется механизм name mangling. К названию приватного метода добавляется также название его класса. Так, приватные методы в родителях на самом деле начинаются с имени родительского класса, а в наследниках - с имени наследника.</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">class User:
def __init__(self):
self.name = "User"
# зададим приватный метод
def __private_greet(self):
return f"Hello, {self.name}!"
# зададим публичный интерфейс для этого метода
def greet(self):
return self.__private_greet()
class John(User):
# попробуем переопределить приватный метод родителя
def __private_greet(self):
return f"Hello, John!"
# метод не изменился
John().greet() # 'Hello, User!'
# посмотрим весь список методов John с помощью функции dir
# здесь мы видим, что к имени приватных методов для защиты от перезаписи добавляется их класс
# так в наследнике есть оригинальный метод и метод наследника
dir(John) # ['_John__private_greet', '_User__private_greet', ..]</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Таким образом мы можем "защитить" родительские методы от изменения в наследниках. Но стоит помнить, что в Python по-настоящему все доступно для разработчика. И добраться до оригинального родительского метода всегда можно обойдя name mangling как <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">_ИмяКласса__имя_метода</code>.</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">class User:
def __init__(self):
self.name = "User"
# зададим приватный метод
def __private_greet(self):
return f"Hello, {self.name}!"
# зададим публичный интерфейс для этого метода
def greet(self):
return self.__private_greet()
class John(User):
# попробуем переопределить приватный метод родителя
# на этот раз доберемся до оригинального метода обойдя name mangling
def _User__private_greet(self):
return f"Hello, John!"
# метод изменился
John().greet() # Hello, John!</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Разумеется, несмотря на то, что мы знаем как добраться до "оригинала" обходить name mangling крайне опасно, ведь если родительский метод был приватным, значит были причины сделать его таким и неизменяемым в наследниках.</p>
<h2 id="heading-2-6">Выбор способа</h2>
<p>Мы только начали знакомиться с наследованием, но уже сейчас видно, что всё не просто. Одну и ту же задачу можно сделать множеством способов. Какой предпочесть?</p>
<p>Универсальная стратегия, которой стоит придерживаться в большинстве случаев, – всегда работать через абстракцию, пока она не мешает. Это значит, что все свойства, которые не предназначены для прямого доступа, делаются с префиксом подчеркивания (соглашение о защищенности), а наружу выставляется публичный интерфейс (методы).</p>
<h2 id="heading-2-7">Выводы</h2>
<p>Чтобы правильно использовать наследование в объектно-ориентированном программировании, необходимо понимать публичные, приватные и защищенные атрибуты и методы. Эти модификаторы доступа позволяют управлять уровнем доступа к функциональности и данным класса, обеспечивая инкапсуляцию и полиморфизм. В итоге это способствует созданию гибкого и надежного кода.</p></div><div style="margin-block:var(--mantine-spacing-xl)" class=""><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md)" class="m_8a5d1357 mantine-Title-root" data-order="2">Рекомендуемые программы</h2><style data-mantine-styles="inline">.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_2mremqrdub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/python?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">10 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Python-разработчик </p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите Python, Django, REST и Fast API для создания веб-приложений</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MzczMSwicHVyIjoiYmxvYl9pZCJ9fQ==--f5df4883f3f678321cb4fa96e9ce657bd5ee1adf/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Static%20website-cuate.png" alt="Python-разработчик " loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 6 792 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/python-oop?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">2 месяца</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">ООП на Python</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите архитектуру и чистый код на Python</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc0OSwicHVyIjoiYmxvYl9pZCJ9fQ==--846349326718432328cf5c0677091aca67f80af3/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-amico%20(1).png" alt="ООП на Python" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div></div></div></div></div><style data-mantine-styles="inline">.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:8.333333333333334%;--col-max-width:8.333333333333334%;}@media(min-width: 48em){.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem" class="m_96bdd299 mantine-Grid-col __m__-_R_1bdub_"><div style="margin-inline:var(--mantine-spacing-xs)" class="mantine-visible-from-sm"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-lg);text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/python-classes/lessons/visibility/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label"><span style="margin-inline-end:var(--mantine-spacing-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дальше</span>→</span></span></a><a style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Навигация по теме</span><span class="m_57492dcc mantine-NavLink-description">Теория</span></div><span class="m_690090b5 mantine-NavLink-section" data-position="right"></span></a><div style="margin-block:var(--mantine-spacing-lg)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div><div style="margin-block:var(--mantine-spacing-lg)" class=""><div style="justify-content:space-between;margin-bottom:calc(0.1875rem * var(--mantine-scale));color:var(--mantine-color-dimmed);font-size:var(--mantine-font-size-xs)" class="m_8bffd616 mantine-Flex-root __m__-_R_qimrbdub_"><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Завершено</p><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0 / 14</p></div><div style="--progress-size:var(--progress-size-sm)" class="m_db6d6462 mantine-Progress-root" data-size="sm"><div style="--progress-section-size:0%;--progress-section-color:var(--mantine-color-gray-filled)" class="m_2242eb65 mantine-Progress-section" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" aria-valuetext="0%"></div></div></div><button style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root" type="button"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Обсуждения (архив)</span><span class="m_57492dcc mantine-NavLink-description"></span></div></button><div style="--toc-bg:var(--mantine-color-blue-light);--toc-color:var(--mantine-color-blue-light-color);--toc-size:var(--mantine-font-size-sm);--toc-radius:var(--mantine-radius-sm);margin-top:var(--mantine-spacing-xl)" class="m_bcaa9990 mantine-TableOfContents-root" data-variant="light" data-size="sm"></div></div><div class="mantine-hidden-from-sm"><div style="--stack-gap:0rem;--stack-align:stretch;--stack-justify:flex-start" class="m_6d731127 mantine-Stack-root"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-xs);padding:0rem;text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/python-classes/lessons/visibility/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">→</span></span></a><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" data-disabled="true" type="button" disabled=""><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></span></button><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto mantine-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" type="button"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></span></button></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-Bukl1lYy.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>