[{"data":1,"prerenderedAt":21685},["ShallowReactive",2],{"navigation_docs":3,"-python-encapsulation":3379,"-python-encapsulation-surround":21680},[4,1707,1912,2366,2547,2649,2856,2978,3028,3085,3119,3245,3322,3375],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"C#","i-devicon-csharp","\u002Fcsharp","01.csharp",[10,13,60,90,120,202,219,253,379,404,457,650,1364,1654,1703],{"title":11,"path":7,"stem":12},"C# та .NET","01.csharp\u002Findex",{"title":14,"icon":15,"path":16,"stem":17,"children":18,"page":59},"Fundamentals","i-lucide-book-open","\u002Fcsharp\u002Ffundamentals","01.csharp\u002F01.fundamentals",[19,23,27,31,35,39,43,47,51,55],{"title":20,"path":21,"stem":22},"Вступ до екосистеми .NET","\u002Fcsharp\u002Ffundamentals\u002Fintroduction-to-ecosystem","01.csharp\u002F01.fundamentals\u002F01.introduction-to-ecosystem",{"title":24,"path":25,"stem":26},"Структура програми на C#","\u002Fcsharp\u002Ffundamentals\u002Fprogram-structure","01.csharp\u002F01.fundamentals\u002F02.program-structure",{"title":28,"path":29,"stem":30},"Змінні та Типи Даних","\u002Fcsharp\u002Ffundamentals\u002Fvariables-data-types","01.csharp\u002F01.fundamentals\u002F03.variables-data-types",{"title":32,"path":33,"stem":34},"Масиви","\u002Fcsharp\u002Ffundamentals\u002Farrays","01.csharp\u002F01.fundamentals\u002F04.arrays",{"title":36,"path":37,"stem":38},"Strings & Text Handling","\u002Fcsharp\u002Ffundamentals\u002Fstrings-text-handling","01.csharp\u002F01.fundamentals\u002F05.strings-text-handling",{"title":40,"path":41,"stem":42},"Дати і Час","\u002Fcsharp\u002Ffundamentals\u002Fdates-time-handling","01.csharp\u002F01.fundamentals\u002F06.dates-time-handling",{"title":44,"path":45,"stem":46},"Потік Керування","\u002Fcsharp\u002Ffundamentals\u002Fcontrol-flow","01.csharp\u002F01.fundamentals\u002F07.control-flow",{"title":48,"path":49,"stem":50},"Методи","\u002Fcsharp\u002Ffundamentals\u002Fmethods","01.csharp\u002F01.fundamentals\u002F08.methods",{"title":52,"path":53,"stem":54},"Основи Відлагодження","\u002Fcsharp\u002Ffundamentals\u002Fdebugging-basics","01.csharp\u002F01.fundamentals\u002F09.debugging-basics",{"title":56,"path":57,"stem":58},"Інтерактивна Консоль (Classic)","\u002Fcsharp\u002Ffundamentals\u002Finteractive-console","01.csharp\u002F01.fundamentals\u002F10.interactive-console",false,{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":59},"OOP","i-lucide-box","\u002Fcsharp\u002Foop","01.csharp\u002F02.oop",[66,70,74,78,82,86],{"title":67,"path":68,"stem":69},"Package Management (Управління Пакетами)","\u002Fcsharp\u002Foop\u002Fpackage-management","01.csharp\u002F02.oop\u002F01.package-management",{"title":71,"path":72,"stem":73},"Класи та Об'єкти","\u002Fcsharp\u002Foop\u002Fclasses-objects","01.csharp\u002F02.oop\u002F02.classes-objects",{"title":75,"path":76,"stem":77},"Властивості та Поля","\u002Fcsharp\u002Foop\u002Fproperties-fields","01.csharp\u002F02.oop\u002F03.properties-fields",{"title":79,"path":80,"stem":81},"Стовпи ООП","\u002Fcsharp\u002Foop\u002Foop-pillars","01.csharp\u002F02.oop\u002F04.oop-pillars",{"title":83,"path":84,"stem":85},"Advanced Types","\u002Fcsharp\u002Foop\u002Fadvanced-types","01.csharp\u002F02.oop\u002F05.advanced-types",{"title":87,"path":88,"stem":89},"Namespaces (Простори Імен)","\u002Fcsharp\u002Foop\u002Fnamespaces","01.csharp\u002F02.oop\u002F06.namespaces",{"title":91,"icon":92,"path":93,"stem":94,"children":95,"page":59},"Advanced Core","i-lucide-zap","\u002Fcsharp\u002Fadvanced-core","01.csharp\u002F03.advanced-core",[96,100,104,108,112,116],{"title":97,"path":98,"stem":99},"Generics (Узагальнення)","\u002Fcsharp\u002Fadvanced-core\u002Fgenerics","01.csharp\u002F03.advanced-core\u002F01.generics",{"title":101,"path":102,"stem":103},"Делегати, Події та Лямбда-вирази","\u002Fcsharp\u002Fadvanced-core\u002Fdelegates-events-lambdas","01.csharp\u002F03.advanced-core\u002F02.delegates-events-lambdas",{"title":105,"path":106,"stem":107},"Interfaces Deep Dive (Інтерфейси: Поглиблений Розгляд)","\u002Fcsharp\u002Fadvanced-core\u002Finterfaces-deep-dive","01.csharp\u002F03.advanced-core\u002F03.interfaces-deep-dive",{"title":109,"path":110,"stem":111},"Обробка Винятків","\u002Fcsharp\u002Fadvanced-core\u002Fexception-handling","01.csharp\u002F03.advanced-core\u002F04.exception-handling",{"title":113,"path":114,"stem":115},"Pattern Matching","\u002Fcsharp\u002Fadvanced-core\u002Fpattern-matching","01.csharp\u002F03.advanced-core\u002F05.pattern-matching",{"title":117,"path":118,"stem":119},"Додаткові Можливості C#","\u002Fcsharp\u002Fadvanced-core\u002Fadditional-features","01.csharp\u002F03.advanced-core\u002F06.additional-features",{"title":121,"icon":122,"path":123,"stem":124,"children":125,"page":59},"Architecture Best Practices","i-lucide-building-2","\u002Fcsharp\u002Farchitecture-best-practices","01.csharp\u002F04.architecture-best-practices",[126,130,149,153,157,161,165,169],{"title":127,"path":128,"stem":129},"Software Design Principles (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fsoftware-design-principles","01.csharp\u002F04.architecture-best-practices\u002F01.software-design-principles",{"title":131,"icon":132,"path":133,"stem":134,"children":135,"page":59},"Design Patterns","i-lucide-folder","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns",[136],{"title":137,"icon":132,"path":138,"stem":139,"children":140,"page":59},"Creational","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational",[141,145],{"title":142,"path":143,"stem":144},"Singleton (Одинак)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fsingleton","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F01.singleton",{"title":146,"path":147,"stem":148},"Builder (Будівельник)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fbuilder","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F02.builder",{"title":150,"path":151,"stem":152},"Building Professional CLIs","\u002Fcsharp\u002Farchitecture-best-practices\u002Fbuilding-professional-clis","01.csharp\u002F04.architecture-best-practices\u002F03.building-professional-clis",{"title":154,"path":155,"stem":156},"Validation & Flow Control","\u002Fcsharp\u002Farchitecture-best-practices\u002Fvalidation-flow-control","01.csharp\u002F04.architecture-best-practices\u002F04.validation-flow-control",{"title":158,"path":159,"stem":160},"The Modern .NET Host (Microsoft.Extensions)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fmodern-dotnet-host","01.csharp\u002F04.architecture-best-practices\u002F05.modern-dotnet-host",{"title":162,"path":163,"stem":164},"Data Mapper: Repository та DAO патерни (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part1","01.csharp\u002F04.architecture-best-practices\u002F06.data-mapper-part1",{"title":166,"path":167,"stem":168},"Data Mapper: Repository та DAO патерни (Частина 2)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part2","01.csharp\u002F04.architecture-best-practices\u002F07.data-mapper-part2",{"title":170,"icon":132,"path":171,"stem":172,"children":173,"page":59},"Di Ioc","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc",[174,178,182,186,190,194,198],{"title":175,"path":176,"stem":177},"Проблема залежностей та Інверсія Контролю","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fthe-dependency-problem","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F01.the-dependency-problem",{"title":179,"path":180,"stem":181},"Будуємо власний Service Container","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fbuild-your-own-container","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F02.build-your-own-container",{"title":183,"path":184,"stem":185},"Service Locator: Паттерн та Анти-паттерн","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-locator-pattern","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F03.service-locator-pattern",{"title":187,"path":188,"stem":189},"Паттерни Dependency Injection","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdependency-injection-patterns","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F04.dependency-injection-patterns",{"title":191,"path":192,"stem":193},"Microsoft DI: IServiceCollection та IServiceProvider","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fmicrosoft-di-deep-dive","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F05.microsoft-di-deep-dive",{"title":195,"path":196,"stem":197},"Service Lifetimes та Scopes","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-lifetimes-and-scopes","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F06.service-lifetimes-and-scopes",{"title":199,"path":200,"stem":201},"DI Анти-паттерни та Найкращі Практики","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdi-anti-patterns-and-best-practices","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F07.di-anti-patterns-and-best-practices",{"title":203,"icon":132,"path":204,"stem":205,"children":206,"page":59},"Standard Library","\u002Fcsharp\u002Fstandard-library","01.csharp\u002F05.standard-library",[207,211,215],{"title":208,"path":209,"stem":210},"Collections (Колекції)","\u002Fcsharp\u002Fstandard-library\u002Fcollections","01.csharp\u002F05.standard-library\u002F01.collections",{"title":212,"path":213,"stem":214},"High Performance Types (Високопродуктивні Типи)","\u002Fcsharp\u002Fstandard-library\u002Fhigh-performance-types","01.csharp\u002F05.standard-library\u002F02.high-performance-types",{"title":216,"path":217,"stem":218},"LINQ (Language Integrated Query)","\u002Fcsharp\u002Fstandard-library\u002Flinq","01.csharp\u002F05.standard-library\u002F03.linq",{"title":220,"icon":221,"path":222,"stem":223,"children":224,"page":59},"System Internals Concurrency","i-lucide-server","\u002Fcsharp\u002Fsystem-internals-concurrency","01.csharp\u002F06.system-internals-concurrency",[225,229,233,237,241,245,249],{"title":226,"path":227,"stem":228},"Memory Management","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmemory-management","01.csharp\u002F06.system-internals-concurrency\u002F01.memory-management",{"title":230,"path":231,"stem":232},"Reflection API: System.Type та Метадані","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Freflection-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F02.reflection-fundamentals",{"title":234,"path":235,"stem":236},"Attributes та Dynamic Language Runtime","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fattributes-dynamic","01.csharp\u002F06.system-internals-concurrency\u002F03.attributes-dynamic",{"title":238,"path":239,"stem":240},"Expression Trees: Швидка Альтернатива Рефлексії","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fexpression-trees-compiled","01.csharp\u002F06.system-internals-concurrency\u002F04.expression-trees-compiled",{"title":242,"path":243,"stem":244},"Source Generators: Compile-Time Code Generation","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsource-generators","01.csharp\u002F06.system-internals-concurrency\u002F05.source-generators",{"title":246,"path":247,"stem":248},"Multithreading Fundamentals","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmultithreading-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F06.multithreading-fundamentals",{"title":250,"path":251,"stem":252},"Synchronization Primitives","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsynchronization-primitives","01.csharp\u002F06.system-internals-concurrency\u002F07.synchronization-primitives",{"title":254,"icon":255,"path":256,"stem":257,"children":258,"page":59},"System Programming Windows","i-lucide-cpu","\u002Fcsharp\u002Fsystem-programming-windows","01.csharp\u002F07.system-programming-windows",[259,263,267,271,275,279,283,287,291,295,299,303,307,311,315,319,323,327,331,335,339,343,347,351,355,359,363,367,371,375],{"title":260,"path":261,"stem":262},"Як Працює Операційна Система","\u002Fcsharp\u002Fsystem-programming-windows\u002Fhow-os-works","01.csharp\u002F07.system-programming-windows\u002F01.how-os-works",{"title":264,"path":265,"stem":266},"Процеси в .NET — API та Запуск","\u002Fcsharp\u002Fsystem-programming-windows\u002Fprocesses-in-dotnet","01.csharp\u002F07.system-programming-windows\u002F02.processes-in-dotnet",{"title":268,"path":269,"stem":270},"Процеси в .NET — IPC та Міжпроцесна Комунікація","\u002Fcsharp\u002Fsystem-programming-windows\u002F02a.processes-ipc","01.csharp\u002F07.system-programming-windows\u002F02a.processes-ipc",{"title":272,"path":273,"stem":274},"Application Domains та Збірки — AppDomain і AssemblyLoadContext","\u002Fcsharp\u002Fsystem-programming-windows\u002Fappdomains-assemblies","01.csharp\u002F07.system-programming-windows\u002F03.appdomains-assemblies",{"title":276,"path":277,"stem":278},"Application Domains та Збірки — Plug-in Система з Hot-Reload","\u002Fcsharp\u002Fsystem-programming-windows\u002F03a.appdomains-plugin-system","01.csharp\u002F07.system-programming-windows\u002F03a.appdomains-plugin-system",{"title":280,"path":281,"stem":282},"Потоки — Основи та API Thread","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-fundamentals","01.csharp\u002F07.system-programming-windows\u002F04.thread-fundamentals",{"title":284,"path":285,"stem":286},"Потоки — Lifecycle, Пріоритети та Безпечне Завершення","\u002Fcsharp\u002Fsystem-programming-windows\u002F04a.thread-lifecycle-priorities","01.csharp\u002F07.system-programming-windows\u002F04a.thread-lifecycle-priorities",{"title":288,"path":289,"stem":290},"Проблеми Спільного Стану — Race Condition та Data Race","\u002Fcsharp\u002Fsystem-programming-windows\u002Fshared-state-problems","01.csharp\u002F07.system-programming-windows\u002F05.shared-state-problems",{"title":292,"path":293,"stem":294},"Проблеми Спільного Стану — Memory Model та volatile","\u002Fcsharp\u002Fsystem-programming-windows\u002F05a.shared-state-memory-model","01.csharp\u002F07.system-programming-windows\u002F05a.shared-state-memory-model",{"title":296,"path":297,"stem":298},"Синхронізація — Monitor, lock та еволюція примітивів","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-fundamentals","01.csharp\u002F07.system-programming-windows\u002F06.synchronization-fundamentals",{"title":300,"path":301,"stem":302},"Синхронізація — Наскрізний Приклад та Deadlock Detection","\u002Fcsharp\u002Fsystem-programming-windows\u002F06a.synchronization-walkthrough","01.csharp\u002F07.system-programming-windows\u002F06a.synchronization-walkthrough",{"title":304,"path":305,"stem":306},"Синхронізація — Mutex, Semaphore та Event-Based Primitives","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-advanced","01.csharp\u002F07.system-programming-windows\u002F07.synchronization-advanced",{"title":308,"path":309,"stem":310},"Синхронізація — Interlocked, Volatile та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002F07a.synchronization-advanced-walkthrough","01.csharp\u002F07.system-programming-windows\u002F07a.synchronization-advanced-walkthrough",{"title":312,"path":313,"stem":314},"Interlocked, CAS та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002Finterlocked-cas-lockfree","01.csharp\u002F07.system-programming-windows\u002F08.interlocked-cas-lockfree",{"title":316,"path":317,"stem":318},"Volatile, Memory Model та Spinning","\u002Fcsharp\u002Fsystem-programming-windows\u002F08a.volatile-memory-model","01.csharp\u002F07.system-programming-windows\u002F08a.volatile-memory-model",{"title":320,"path":321,"stem":322},"ThreadPool — Пул Потоків для Ефективного Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-pool","01.csharp\u002F07.system-programming-windows\u002F09.thread-pool",{"title":324,"path":325,"stem":326},"ThreadPool — Просунуті Сценарії та Внутрішня Будова","\u002Fcsharp\u002Fsystem-programming-windows\u002F09a.thread-pool-advanced","01.csharp\u002F07.system-programming-windows\u002F09a.thread-pool-advanced",{"title":328,"path":329,"stem":330},"Concurrent та Immutable Collections","\u002Fcsharp\u002Fsystem-programming-windows\u002Fconcurrent-collections","01.csharp\u002F07.system-programming-windows\u002F10.concurrent-collections",{"title":332,"path":333,"stem":334},"TPL, Task та Композиція — Від Thread до Task","\u002Fcsharp\u002Fsystem-programming-windows\u002Ftpl-parallel-plinq","01.csharp\u002F07.system-programming-windows\u002F11.tpl-parallel-plinq",{"title":336,"path":337,"stem":338},"Parallel Class та PLINQ — Data Parallelism","\u002Fcsharp\u002Fsystem-programming-windows\u002F11a.tpl-parallel-plinq-advanced","01.csharp\u002F07.system-programming-windows\u002F11a.tpl-parallel-plinq-advanced",{"title":340,"path":341,"stem":342},"Async\u002FAwait — Фундамент Асинхронного Програмування","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-fundamentals","01.csharp\u002F07.system-programming-windows\u002F12.async-fundamentals",{"title":344,"path":345,"stem":346},"SynchronizationContext та ConfigureAwait — Контекст Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-context-configureawait","01.csharp\u002F07.system-programming-windows\u002F13.async-context-configureawait",{"title":348,"path":349,"stem":350},"Async — Просунуті Паттерни","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-advanced","01.csharp\u002F07.system-programming-windows\u002F14.async-advanced",{"title":352,"path":353,"stem":354},"System.Threading.Channels — Async Producer-Consumer","\u002Fcsharp\u002Fsystem-programming-windows\u002Fchannels","01.csharp\u002F07.system-programming-windows\u002F15.channels",{"title":356,"path":357,"stem":358},"Асинхронна Синхронізація","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-synchronization","01.csharp\u002F07.system-programming-windows\u002F16.async-synchronization",{"title":360,"path":361,"stem":362},"Unsafe Code та Вказівники","\u002Fcsharp\u002Fsystem-programming-windows\u002Funsafe-code","01.csharp\u002F07.system-programming-windows\u002F17.unsafe-code",{"title":364,"path":365,"stem":366},"P\u002FInvoke та Windows API — Міст між .NET та Native Code","\u002Fcsharp\u002Fsystem-programming-windows\u002Fpinvoke-winapi","01.csharp\u002F07.system-programming-windows\u002F18.pinvoke-winapi",{"title":368,"path":369,"stem":370},"Реєстр Windows — Центральна База Конфігурації Системи","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-registry","01.csharp\u002F07.system-programming-windows\u002F19.windows-registry",{"title":372,"path":373,"stem":374},"Windows Hooks, Hotkeys та Services — Глибока Інтеграція з ОС","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-hooks-services","01.csharp\u002F07.system-programming-windows\u002F20.windows-hooks-services",{"title":376,"path":377,"stem":378},"Системне Програмування C# (Windows) — 07.system-programming-windows","\u002Fcsharp\u002Fsystem-programming-windows\u002Fimplementation_plan","01.csharp\u002F07.system-programming-windows\u002Fimplementation_plan",{"title":380,"icon":132,"path":381,"stem":382,"children":383,"page":59},"Io","\u002Fcsharp\u002Fio","01.csharp\u002F08.io",[384,388,392,396,400],{"title":385,"path":386,"stem":387},"8.1.1. Основи роботи з файловою системою","\u002Fcsharp\u002Fio\u002Ffile-system-basics","01.csharp\u002F08.io\u002F01.file-system-basics",{"title":389,"path":390,"stem":391},"8.1.2. Потоки (Streams) та Серіалізація Даних","\u002Fcsharp\u002Fio\u002Fstreams-serialization","01.csharp\u002F08.io\u002F02.streams-serialization",{"title":393,"path":394,"stem":395},"8.2.1. JSON Serialization з System.Text.Json","\u002Fcsharp\u002Fio\u002Fjson-serialization","01.csharp\u002F08.io\u002F03.json-serialization",{"title":397,"path":398,"stem":399},"8.2.2. XML Serialization та LINQ to XML","\u002Fcsharp\u002Fio\u002Fxml-serialization","01.csharp\u002F08.io\u002F04.xml-serialization",{"title":401,"path":402,"stem":403},"8.2.3. Binary Serialization: MessagePack та Protocol Buffers","\u002Fcsharp\u002Fio\u002Fbinary-serialization","01.csharp\u002F08.io\u002F05.binary-serialization",{"title":405,"icon":132,"path":406,"stem":407,"children":408,"page":59},"Ado Net","\u002Fcsharp\u002Fado-net","01.csharp\u002F09.ado-net",[409,413,417,421,425,429,433,437,441,445,449,453],{"title":410,"path":411,"stem":412},"9.1. Введення в ADO.NET","\u002Fcsharp\u002Fado-net\u002Fintroduction-to-adonet","01.csharp\u002F09.ado-net\u002F01.introduction-to-adonet",{"title":414,"path":415,"stem":416},"9.2. Клас DbConnection — з'єднання з базою даних","\u002Fcsharp\u002Fado-net\u002Fconnection","01.csharp\u002F09.ado-net\u002F02.connection",{"title":418,"path":419,"stem":420},"9.3. Клас DbCommand — виконання SQL-запитів","\u002Fcsharp\u002Fado-net\u002Fcommand-and-queries","01.csharp\u002F09.ado-net\u002F03.command-and-queries",{"title":422,"path":423,"stem":424},"9.4. Клас DbDataReader — ефективне читання даних","\u002Fcsharp\u002Fado-net\u002Fdatareader","01.csharp\u002F09.ado-net\u002F04.datareader",{"title":426,"path":427,"stem":428},"9.5. Параметризовані запити та захист від SQL Injection","\u002Fcsharp\u002Fado-net\u002Fparameters-and-sql-injection","01.csharp\u002F09.ado-net\u002F05.parameters-and-sql-injection",{"title":430,"path":431,"stem":432},"9.6. Транзакції в ADO.NET","\u002Fcsharp\u002Fado-net\u002Ftransactions","01.csharp\u002F09.ado-net\u002F06.transactions",{"title":434,"path":435,"stem":436},"9.7. DbProviderFactory — провайдер-незалежний код","\u002Fcsharp\u002Fado-net\u002Fprovider-factory","01.csharp\u002F09.ado-net\u002F07.provider-factory",{"title":438,"path":439,"stem":440},"9.8. Асинхронний доступ до даних","\u002Fcsharp\u002Fado-net\u002Fasync-data-access","01.csharp\u002F09.ado-net\u002F08.async-data-access",{"title":442,"path":443,"stem":444},"9.9. Від'єднаний режим: DataSet, DataTable, DataRow","\u002Fcsharp\u002Fado-net\u002Fdisconnected-mode-dataset","01.csharp\u002F09.ado-net\u002F09.disconnected-mode-dataset",{"title":446,"path":447,"stem":448},"9.10. DataAdapter — міст між DataSet та базою даних","\u002Fcsharp\u002Fado-net\u002Fdata-adapter","01.csharp\u002F09.ado-net\u002F10.data-adapter",{"title":450,"path":451,"stem":452},"9.11. Data Mapper та Repository: Архітектура доступу до даних","\u002Fcsharp\u002Fado-net\u002Fdata-mapper-repository","01.csharp\u002F09.ado-net\u002F11.data-mapper-repository",{"title":454,"path":455,"stem":456},"9.12. Identity Map, Unit of Work та Specification Pattern","\u002Fcsharp\u002Fado-net\u002Fadvanced-patterns","01.csharp\u002F09.ado-net\u002F12.advanced-patterns",{"title":458,"icon":255,"path":459,"stem":460,"children":461,"page":59},"Ef Core","\u002Fcsharp\u002Fef-core","01.csharp\u002F10.ef-core",[462,466,470,474,478,482,486,490,494,498,502,506,510,514,518,522,526,532,538,542,546,550,554,558,562,566,570,574,578,582,586,590,594,598,602,606,610,614,618,622,626,630,634,638,642,646],{"title":463,"path":464,"stem":465},"Що таке ORM? Від SQL до об'єктів","\u002Fcsharp\u002Fef-core\u002Fwhat-is-orm","01.csharp\u002F10.ef-core\u002F01.what-is-orm",{"title":467,"path":468,"stem":469},"Перший проєкт — від нуля до CRUD","\u002Fcsharp\u002Fef-core\u002Ffirst-project","01.csharp\u002F10.ef-core\u002F02.first-project",{"title":471,"path":472,"stem":473},"DbContext — Серце EF Core","\u002Fcsharp\u002Fef-core\u002Fdbcontext-deep-dive","01.csharp\u002F10.ef-core\u002F03.dbcontext-deep-dive",{"title":475,"path":476,"stem":477},"Провайдери баз даних — Архітектура та Вибір СУБД","\u002Fcsharp\u002Fef-core\u002Fdatabase-providers","01.csharp\u002F10.ef-core\u002F04.database-providers",{"title":479,"path":480,"stem":481},"Конвенції EF Core — Магія без конфігурації","\u002Fcsharp\u002Fef-core\u002Fconventions","01.csharp\u002F10.ef-core\u002F05.conventions",{"title":483,"path":484,"stem":485},"Fluent API та Data Annotations — Явна конфігурація моделі","\u002Fcsharp\u002Fef-core\u002Ffluent-api-vs-annotations","01.csharp\u002F10.ef-core\u002F06.fluent-api-vs-annotations",{"title":487,"path":488,"stem":489},"Зв'язки — One-to-One та One-to-Many","\u002Fcsharp\u002Fef-core\u002Frelationships-basics","01.csharp\u002F10.ef-core\u002F07.relationships-basics",{"title":491,"path":492,"stem":493},"Зв'язки Advanced — Many-to-Many та Складні Сценарії","\u002Fcsharp\u002Fef-core\u002Frelationships-advanced","01.csharp\u002F10.ef-core\u002F08.relationships-advanced",{"title":495,"path":496,"stem":497},"Властивості — Типи, Конвертери, Компаратори (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part1","01.csharp\u002F10.ef-core\u002F09.property-configuration-part1",{"title":499,"path":500,"stem":501},"Властивості — Value Comparers, Generators, Shadow Properties (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part2","01.csharp\u002F10.ef-core\u002F09.property-configuration-part2",{"title":503,"path":504,"stem":505},"Складні типи — Owned Types та Complex Types (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part1","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part1",{"title":507,"path":508,"stem":509},"Складні типи — Complex Types, Keyless Entities, Порівняння (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part2","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part2",{"title":511,"path":512,"stem":513},"JSON Columns — Складні дані у JSON (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part1","01.csharp\u002F10.ef-core\u002F11.json-columns-part1",{"title":515,"path":516,"stem":517},"JSON Columns — Value Comparers, Індекси, Провайдери (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part2","01.csharp\u002F10.ef-core\u002F11.json-columns-part2",{"title":519,"path":520,"stem":521},"Успадкування — Абстрактні класи та TPH (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finheritance-part1","01.csharp\u002F10.ef-core\u002F12.inheritance-part1",{"title":523,"path":524,"stem":525},"Успадкування — TPT, TPC та Порівняння Стратегій (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finheritance-part2","01.csharp\u002F10.ef-core\u002F12.inheritance-part2",{"title":527,"path":528,"stem":529,"children":530},"Індекси, Обмеження та Схема (Частина 1)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part1","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part1",[531],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"Індекси, Обмеження та Схема (Частина 2)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part2","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part2",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541},"Seed Data — Початкові Дані (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fseeding-part1","01.csharp\u002F10.ef-core\u002F14.seeding-part1",{"title":543,"path":544,"stem":545},"Seed Data — SQL-скрипти, Bogus та Стратегії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fseeding-part2","01.csharp\u002F10.ef-core\u002F14.seeding-part2",{"title":547,"path":548,"stem":549},"Global Query Filters — Глобальні Фільтри (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part1","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part1",{"title":551,"path":552,"stem":553},"Global Query Filters — Підводні камені та Інтеграція (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part2","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part2",{"title":555,"path":556,"stem":557},"LINQ-запити в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part1","01.csharp\u002F10.ef-core\u002F16.linq-queries-part1",{"title":559,"path":560,"stem":561},"LINQ-запити в EF Core (Частина 2)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part2","01.csharp\u002F10.ef-core\u002F16.linq-queries-part2",{"title":563,"path":564,"stem":565},"Завантаження Пов'язаних Даних (Частина 1)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part1","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part1",{"title":567,"path":568,"stem":569},"Завантаження Пов'язаних Даних (Частина 2)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part2","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part2",{"title":571,"path":572,"stem":573},"Raw SQL, Views та Stored Procedures (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part1","01.csharp\u002F10.ef-core\u002F18.raw-sql-part1",{"title":575,"path":576,"stem":577},"Raw SQL — Stored Procedures, DbFunction та Bulk Operations (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part2","01.csharp\u002F10.ef-core\u002F18.raw-sql-part2",{"title":579,"path":580,"stem":581},"Продвинуті Запити — Compiled Queries, Bulk та Оптимізація (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part1","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part1",{"title":583,"path":584,"stem":585},"Продвинуті Запити — Query Tags, Bulk та Interceptors (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part2","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part2",{"title":587,"path":588,"stem":589},"Change Tracker — Відстеження Змін (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part1","01.csharp\u002F10.ef-core\u002F20.change-tracking-part1",{"title":591,"path":592,"stem":593},"Change Tracker — Графи Об'єктів та Disconnected (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part2","01.csharp\u002F10.ef-core\u002F20.change-tracking-part2",{"title":595,"path":596,"stem":597},"Збереження Даних та Транзакції (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part1","01.csharp\u002F10.ef-core\u002F21.saving-data-part1",{"title":599,"path":600,"stem":601},"Збереження Даних — Concurrency та Outbox (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part2","01.csharp\u002F10.ef-core\u002F21.saving-data-part2",{"title":603,"path":604,"stem":605},"Конкурентність та Блокування (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part1","01.csharp\u002F10.ef-core\u002F22.concurrency-part1",{"title":607,"path":608,"stem":609},"Конкурентність — Дедлоки та Queue Processing (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part2","01.csharp\u002F10.ef-core\u002F22.concurrency-part2",{"title":611,"path":612,"stem":613},"Міграції в EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part1","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part1",{"title":615,"path":616,"stem":617},"Міграції в EF Core — Основи (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part2","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part2",{"title":619,"path":620,"stem":621},"Міграції — Просунуті Сценарії (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part1","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part1",{"title":623,"path":624,"stem":625},"Міграції — Просунуті Сценарії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part2","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part2",{"title":627,"path":628,"stem":629},"Управління Схемою та Database-First (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part1","01.csharp\u002F10.ef-core\u002F25.schema-management-part1",{"title":631,"path":632,"stem":633},"Управління Схемою та Database-First (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part2","01.csharp\u002F10.ef-core\u002F25.schema-management-part2",{"title":635,"path":636,"stem":637},"Продуктивність EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fperformance-fundamentals-part1","01.csharp\u002F10.ef-core\u002F26.performance-fundamentals-part1",{"title":639,"path":640,"stem":641},"Interceptors в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part1","01.csharp\u002F10.ef-core\u002F29.interceptors-part1",{"title":643,"path":644,"stem":645},"Interceptors в EF Core — Connection, Transaction та Materialization (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part2","01.csharp\u002F10.ef-core\u002F29.interceptors-part2",{"title":647,"path":648,"stem":649},"План вивчення Entity Framework Core — Повний курс","\u002Fcsharp\u002Fef-core\u002Fimplementation_plan","01.csharp\u002F10.ef-core\u002Fimplementation_plan",{"title":651,"icon":652,"path":653,"stem":654,"children":655,"page":59},"ASP.NET","i-devicon-dotnetcore","\u002Fcsharp\u002Faspnet","01.csharp\u002F11.aspnet",[656,730,791,869,927,941,967,1057,1111,1182,1212,1289,1346],{"title":657,"icon":658,"path":659,"stem":660,"children":661,"page":59},"Minimal API","i-lucide-network","\u002Fcsharp\u002Faspnet\u002Fminimal-api","01.csharp\u002F11.aspnet\u002F01.minimal-api",[662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726],{"title":663,"path":664,"stem":665},"Вступ до ASP.NET та еволюція фреймворку","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fintroduction","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F01.introduction",{"title":667,"path":668,"stem":669},"Перший додаток на ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Ffirst-application","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F02.first-application",{"title":671,"path":672,"stem":673},"WebApplication, Builder та Dependency Injection","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fwebapplication-builder","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F03.webapplication-builder",{"title":675,"path":676,"stem":677},"Конвеєр запитів та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frequest-pipeline-middleware","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F04.request-pipeline-middleware",{"title":679,"path":680,"stem":681},"Маршрутизація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F05.routing-basics",{"title":683,"path":684,"stem":685},"Маршрутизація в ASP.NET Core: Розширені можливості","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F06.routing-advanced",{"title":687,"path":688,"stem":689},"Статичні файли в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-files","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F07.static-files",{"title":691,"path":692,"stem":693},"Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-assets","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F08.static-assets",{"title":695,"path":696,"stem":697},"Конфігурація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-fundamentals","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F09.configuration-fundamentals",{"title":699,"path":700,"stem":701},"Конфігурація: Паттерн Options","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-options","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F10.configuration-options",{"title":703,"path":704,"stem":705},"Логування в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F11.logging-basics",{"title":707,"path":708,"stem":709},"Логування: Serilog та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F12.logging-advanced",{"title":711,"path":712,"stem":713},"Управління станом: HttpContext.Items та Cookies","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstate-management","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F13.state-management",{"title":715,"path":716,"stem":717},"Стан сесії: Sessions","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fsession-state","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F14.session-state",{"title":719,"path":720,"stem":721},"Структура проєкту: від хаосу до архітектури","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fproject-structure","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F15.project-structure",{"title":723,"path":724,"stem":725},"Scalar у Minimal API: повний проєкт і Fluent OpenAPI","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fscalar-openapi-fluent","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F16.scalar-openapi-fluent",{"title":727,"path":728,"stem":729},"Swagger \u002F Swashbuckle у Minimal API: окремий класичний шлях","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fswagger-swashbuckle","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F17.swagger-swashbuckle",{"title":731,"icon":658,"path":732,"stem":733,"children":734,"page":59},"API","\u002Fcsharp\u002Faspnet\u002Fapi","01.csharp\u002F11.aspnet\u002F02.api",[735,739,743,747,751,755,759,763,767,771,775,779,783,787],{"title":736,"path":737,"stem":738},"Що таке API. Клієнт-серверна архітектура","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fwhat-is-api","01.csharp\u002F11.aspnet\u002F02.api\u002F01.what-is-api",{"title":740,"path":741,"stem":742},"Формати даних: JSON, XML, TOML та бінарні формати","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fdata-formats","01.csharp\u002F11.aspnet\u002F02.api\u002F02.data-formats",{"title":744,"path":745,"stem":746},"Парадигми API та концепція REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-paradigms-rest","01.csharp\u002F11.aspnet\u002F02.api\u002F03.api-paradigms-rest",{"title":748,"path":749,"stem":750},"HTTP-методи, статус-коди та заголовки","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fhttp-methods-status-codes","01.csharp\u002F11.aspnet\u002F02.api\u002F04.http-methods-status-codes",{"title":752,"path":753,"stem":754},"Організація HTTP API за принципами REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Frest-organizing","01.csharp\u002F11.aspnet\u002F02.api\u002F05.rest-organizing",{"title":756,"path":757,"stem":758},"Номенклатура URL та CRUD-операції","\u002Fcsharp\u002Faspnet\u002Fapi\u002Furl-nomenclature-crud","01.csharp\u002F11.aspnet\u002F02.api\u002F06.url-nomenclature-crud",{"title":760,"path":761,"stem":762},"Правила дизайну: іменування та стандарти","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-naming","01.csharp\u002F11.aspnet\u002F02.api\u002F07.api-design-naming",{"title":764,"path":765,"stem":766},"Валідація, ліміти та обробка помилок","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-validation","01.csharp\u002F11.aspnet\u002F02.api\u002F08.api-design-validation",{"title":768,"path":769,"stem":770},"Обробка помилок у Minimal API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Ferror-handling-http","01.csharp\u002F11.aspnet\u002F02.api\u002F09.error-handling-http",{"title":772,"path":773,"stem":774},"Ідемпотентність та синхронізація стану","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fidempotency-sync","01.csharp\u002F11.aspnet\u002F02.api\u002F10.idempotency-sync",{"title":776,"path":777,"stem":778},"Пагінація та організація списків","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fpagination-lists","01.csharp\u002F11.aspnet\u002F02.api\u002F11.pagination-lists",{"title":780,"path":781,"stem":782},"Безпека API, кешування та інтернаціоналізація","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fsecurity-auth","01.csharp\u002F11.aspnet\u002F02.api\u002F12.security-auth",{"title":784,"path":785,"stem":786},"Процес проєктування API та документування","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-process","01.csharp\u002F11.aspnet\u002F02.api\u002F13.api-design-process",{"title":788,"path":789,"stem":790},"OpenAPI: контракт, специфікація та документація API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fopenapi","01.csharp\u002F11.aspnet\u002F02.api\u002F14.openapi",{"title":792,"icon":793,"path":794,"stem":795,"children":796,"page":59},"Auth","i-lucide-shield-check","\u002Fcsharp\u002Faspnet\u002Fauth","01.csharp\u002F11.aspnet\u002F03.auth",[797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865],{"title":798,"path":799,"stem":800},"Основи аутентифікації та авторизації","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauth-fundamentals","01.csharp\u002F11.aspnet\u002F03.auth\u002F01.auth-fundamentals",{"title":802,"path":803,"stem":804},"JWT-аутентифікація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fjwt-authentication","01.csharp\u002F11.aspnet\u002F03.auth\u002F02.jwt-authentication",{"title":806,"path":807,"stem":808},"Авторизація: ролі, політики та resource-based доступ","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauthorization-policies","01.csharp\u002F11.aspnet\u002F03.auth\u002F03.authorization-policies",{"title":810,"path":811,"stem":812},"Cookie-аутентифікація та ASP.NET Core Identity","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcookie-auth-identity","01.csharp\u002F11.aspnet\u002F03.auth\u002F04.cookie-auth-identity",{"title":814,"path":815,"stem":816},"JWT + Refresh Tokens (HttpOnly Cookie)","\u002Fcsharp\u002Faspnet\u002Fauth\u002F04b.identity-auth-jwt","01.csharp\u002F11.aspnet\u002F03.auth\u002F04b.identity-auth-jwt",{"title":818,"path":819,"stem":820},"Identity: Підтвердження Email та Скидання Пароля","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-email-confirmation","01.csharp\u002F11.aspnet\u002F03.auth\u002F05.identity-email-confirmation",{"title":822,"path":823,"stem":824},"Identity: Двофакторна Аутентифікація (2FA)","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-two-factor","01.csharp\u002F11.aspnet\u002F03.auth\u002F06.identity-two-factor",{"title":826,"path":827,"stem":828},"Identity: Внутрішня Архітектура та Кастомізація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-internals","01.csharp\u002F11.aspnet\u002F03.auth\u002F07.identity-internals",{"title":830,"path":831,"stem":832},"OAuth 2.0 та зовнішні провайдери","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-external-providers","01.csharp\u002F11.aspnet\u002F03.auth\u002F08.oauth-external-providers",{"title":834,"path":835,"stem":836},"Безпека на практиці: CORS, HTTPS та захист від атак","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fsecurity-hardening","01.csharp\u002F11.aspnet\u002F03.auth\u002F09.security-hardening",{"title":838,"path":839,"stem":840},"Теорія OAuth 2.0: Поняття, Аналогії та Флоу","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-theory","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oauth-theory",{"title":842,"path":843,"stem":844},"OIDC, OAuth 2.0 та Keycloak в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foidc-keycloak","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oidc-keycloak",{"title":846,"path":847,"stem":848},"API Keys аутентифікація в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fapi-keys","01.csharp\u002F11.aspnet\u002F03.auth\u002F11.api-keys",{"title":850,"path":851,"stem":852},"Rate Limiting та Throttling в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frate-limiting","01.csharp\u002F11.aspnet\u002F03.auth\u002F12.rate-limiting",{"title":854,"path":855,"stem":856},"Refresh Token Rotation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frefresh-token-rotation","01.csharp\u002F11.aspnet\u002F03.auth\u002F13.refresh-token-rotation",{"title":858,"path":859,"stem":860},"Certificate Authentication та mTLS в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcertificate-auth","01.csharp\u002F11.aspnet\u002F03.auth\u002F14.certificate-auth",{"title":862,"path":863,"stem":864},"RBAC, ABAC та ReBAC в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frbac-abac-rebac","01.csharp\u002F11.aspnet\u002F03.auth\u002F15.rbac-abac-rebac",{"title":866,"path":867,"stem":868},"Multi-tenancy та ізоляція даних в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fmulti-tenancy","01.csharp\u002F11.aspnet\u002F03.auth\u002F16.multi-tenancy",{"title":870,"icon":871,"path":872,"stem":873,"children":874,"page":59},"Нотифікації","i-lucide-bell","\u002Fcsharp\u002Faspnet\u002Fnotifications","01.csharp\u002F11.aspnet\u002F04.notifications",[875,879,883,887,891,895,899,903,907,911,915,919,923],{"title":876,"path":877,"stem":878},"In-App нотифікації через базу даних","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fin-app-database-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F01.in-app-database-notifications",{"title":880,"path":881,"stem":882},"Polling: Регулярний запит оновлень","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fpolling","01.csharp\u002F11.aspnet\u002F04.notifications\u002F02.polling",{"title":884,"path":885,"stem":886},"Server-Sent Events: Однострімовий push від сервера","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fserver-sent-events","01.csharp\u002F11.aspnet\u002F04.notifications\u002F03.server-sent-events",{"title":888,"path":889,"stem":890},"WebSockets: Двостороннє з'єднання в реальному часі","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fwebsockets","01.csharp\u002F11.aspnet\u002F04.notifications\u002F04.websockets",{"title":892,"path":893,"stem":894},"SignalR: Абстракція над транспортами реального часу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fsignalr","01.csharp\u002F11.aspnet\u002F04.notifications\u002F05.signalr",{"title":896,"path":897,"stem":898},"Background Services: Фонові задачі в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fbackground-services","01.csharp\u002F11.aspnet\u002F04.notifications\u002F06.background-services",{"title":900,"path":901,"stem":902},"Web Push нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fweb-push","01.csharp\u002F11.aspnet\u002F04.notifications\u002F07.web-push",{"title":904,"path":905,"stem":906},"Email нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Femail-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F08.email-notifications",{"title":908,"path":909,"stem":910},"Порівняння підходів: Як вибрати правильну технологію нотифікацій","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fchoosing-the-right-approach","01.csharp\u002F11.aspnet\u002F04.notifications\u002F09.choosing-the-right-approach",{"title":912,"path":913,"stem":914},"Hangfire: Надійне планування фонових задач","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire","01.csharp\u002F11.aspnet\u002F04.notifications\u002F10.hangfire",{"title":916,"path":917,"stem":918},"Практика: Конвертація зображень у WebP через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-image-webp","01.csharp\u002F11.aspnet\u002F04.notifications\u002F11.hangfire-image-webp",{"title":920,"path":921,"stem":922},"Практика: Підготовка відео до HLS-стрімінгу через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-video-hls","01.csharp\u002F11.aspnet\u002F04.notifications\u002F12.hangfire-video-hls",{"title":924,"path":925,"stem":926},"Telegram-нотифікації: від одного повідомлення до масових розсилок і мульти-канального підходу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Ftelegram-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F13.telegram-notifications",{"title":928,"icon":929,"path":930,"stem":931,"children":932,"page":59},"Інтернаціоналізація","i-lucide-languages","\u002Fcsharp\u002Faspnet\u002Fi18n","01.csharp\u002F11.aspnet\u002F05.i18n",[933,937],{"title":934,"path":935,"stem":936},"Інтернаціоналізація (i18n) у Minimal API: від A до Я","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Finternationalization","01.csharp\u002F11.aspnet\u002F05.i18n\u002F01.internationalization",{"title":938,"path":939,"stem":940},"Humanizer: людиномовні рядки у .NET","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Fhumanizer","01.csharp\u002F11.aspnet\u002F05.i18n\u002F02.humanizer",{"title":942,"icon":943,"path":944,"stem":945,"children":946,"page":59},"Кешування","i-lucide-layers","\u002Fcsharp\u002Faspnet\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching",[947,951,955,959,963],{"title":948,"path":949,"stem":950},"Огляд кешування: чотири рівні і коли що обирати","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching\u002F01.caching",{"title":952,"path":953,"stem":954},"IMemoryCache: кеш в оперативній пам'яті","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fmemory-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F02.memory-cache",{"title":956,"path":957,"stem":958},"IDistributedCache і Redis: розподілений кеш","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fdistributed-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F03.distributed-cache",{"title":960,"path":961,"stem":962},"Response Cache: HTTP-кешування через Cache-Control","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fresponse-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F04.response-cache",{"title":964,"path":965,"stem":966},"Output Cache: серверний кеш HTTP-відповідей (.NET 7+)","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Foutput-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F05.output-cache",{"title":968,"icon":969,"path":970,"stem":971,"children":972,"page":59},"Тестування","i-lucide-test-tube","\u002Fcsharp\u002Faspnet\u002Ftesting","01.csharp\u002F11.aspnet\u002F07.testing",[973,977,981,985,989,993,997,1001,1005,1009,1013,1017,1021,1025,1029,1033,1037,1041,1045,1049,1053],{"title":974,"path":975,"stem":976},"Що таке тестування? Від інтуїції до науки","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-is-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F01.what-is-testing",{"title":978,"path":979,"stem":980},"Піраміда тестування — Стратегія, а не Догма","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-pyramid","01.csharp\u002F11.aspnet\u002F07.testing\u002F02.testing-pyramid",{"title":982,"path":983,"stem":984},"Дві Школи Тестування — Лондон проти Детройту","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-schools","01.csharp\u002F11.aspnet\u002F07.testing\u002F03.testing-schools",{"title":986,"path":987,"stem":988},"TDD та BDD — Тести як Дизайн-інструмент","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftdd-and-bdd","01.csharp\u002F11.aspnet\u002F07.testing\u002F04.tdd-and-bdd",{"title":990,"path":991,"stem":992},"Що саме тестувати — Техніки аналізу та Циклomatична складність","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-to-test","01.csharp\u002F11.aspnet\u002F07.testing\u002F05.what-to-test",{"title":994,"path":995,"stem":996},"Тестові Фреймворки — Навіщо вони і що всередині","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftest-frameworks","01.csharp\u002F11.aspnet\u002F07.testing\u002F06.test-frameworks",{"title":998,"path":999,"stem":1000},"xUnit — Факти, Теорії та Lifecycle тестів","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-basics","01.csharp\u002F11.aspnet\u002F07.testing\u002F07.xunit-basics",{"title":1002,"path":1003,"stem":1004},"xUnit Advanced — Fixtures, Кастомізація та Розширення","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F08.xunit-advanced",{"title":1006,"path":1007,"stem":1008},"Moq — Глибоке занурення в мокування","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fmocking-with-moq","01.csharp\u002F11.aspnet\u002F07.testing\u002F09.mocking-with-moq",{"title":1010,"path":1011,"stem":1012},"Тестування Баз Даних — EF Core, SQLite та Testcontainers","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fdatabase-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F10.database-testing",{"title":1014,"path":1015,"stem":1016},"Integration Testing — Частина 1 [Теорія та WebApplicationFactory]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F11.integration-testing",{"title":1018,"path":1019,"stem":1020},"Інтеграційне тестування — Практика","\u002Fcsharp\u002Faspnet\u002Ftesting\u002F11a.integration-testing-practice","01.csharp\u002F11.aspnet\u002F07.testing\u002F11a.integration-testing-practice",{"title":1022,"path":1023,"stem":1024},"Integration Testing — Частина 2 [Просунуті Сценарії та Testcontainers]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F12.integration-testing-advanced",{"title":1026,"path":1027,"stem":1028},"Професійний Postman: Колекції, Змінні та GitHub Інтеграція","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fpostman-professional","01.csharp\u002F11.aspnet\u002F07.testing\u002F13.postman-professional",{"title":1030,"path":1031,"stem":1032},"HttpClient у Тестах Частина 1: Архітектура та MockHttpMessageHandler","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fhttpclient-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F14.httpclient-testing",{"title":1034,"path":1035,"stem":1036},"HttpClient у Тестах Частина 2: WireMock.Net та Resilience","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwiremock-net","01.csharp\u002F11.aspnet\u002F07.testing\u002F15.wiremock-net",{"title":1038,"path":1039,"stem":1040},"Патерни та Анти-патерни Тестування: Test Smells","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-patterns","01.csharp\u002F11.aspnet\u002F07.testing\u002F16.testing-patterns",{"title":1042,"path":1043,"stem":1044},"Просунуті інструменти: Time, Snapshots та Властивості","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fadvanced-testing-tools","01.csharp\u002F11.aspnet\u002F07.testing\u002F17.advanced-testing-tools",{"title":1046,"path":1047,"stem":1048},"Тестування Архітектури з NetArchTest","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Farchitecture-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F18.architecture-testing",{"title":1050,"path":1051,"stem":1052},"Тестування Продуктивності: BenchmarkDotNet, NBomber та k6","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fperformance-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F19.performance-testing",{"title":1054,"path":1055,"stem":1056},"Залишок плану для курсу \"Тестування ASP.NET Minimal API\"","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fremaining_plan","01.csharp\u002F11.aspnet\u002F07.testing\u002Fremaining_plan",{"title":1058,"icon":1059,"path":1060,"stem":1061,"children":1062,"page":59},"Платежі","i-lucide-credit-card","\u002Fcsharp\u002Faspnet\u002Fpayments","01.csharp\u002F11.aspnet\u002F08.payments",[1063,1067,1071,1075,1079,1083,1087,1091,1095,1099,1103,1107],{"title":1064,"path":1065,"stem":1066},"Основи платіжної інфраструктури","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-fundamentals","01.csharp\u002F11.aspnet\u002F08.payments\u002F01.payment-fundamentals",{"title":1068,"path":1069,"stem":1070},"Методи оплати в Україні","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-methods-ukraine","01.csharp\u002F11.aspnet\u002F08.payments\u002F02.payment-methods-ukraine",{"title":1072,"path":1073,"stem":1074},"PCI DSS та безпека платежів","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpci-dss-security","01.csharp\u002F11.aspnet\u002F08.payments\u002F03.pci-dss-security",{"title":1076,"path":1077,"stem":1078},"Архітектура платіжної підсистеми","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-architecture","01.csharp\u002F11.aspnet\u002F08.payments\u002F04.payment-architecture",{"title":1080,"path":1081,"stem":1082},"Інтеграція LiqPay (ПриватБанк)","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fliqpay-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F05.liqpay-integration",{"title":1084,"path":1085,"stem":1086},"Інтеграція Monobank Acquiring API","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fmonobank-acquiring","01.csharp\u002F11.aspnet\u002F08.payments\u002F06.monobank-acquiring",{"title":1088,"path":1089,"stem":1090},"Інтеграція Stripe","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fstripe-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F07.stripe-integration",{"title":1092,"path":1093,"stem":1094},"Webhooks — глибоке занурення","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fwebhooks-deep-dive","01.csharp\u002F11.aspnet\u002F08.payments\u002F08.webhooks-deep-dive",{"title":1096,"path":1097,"stem":1098},"Підписки та рекурентні платежі","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fsubscriptions-recurring","01.csharp\u002F11.aspnet\u002F08.payments\u002F09.subscriptions-recurring",{"title":1100,"path":1101,"stem":1102},"Повернення коштів та диспути","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Frefunds-disputes","01.csharp\u002F11.aspnet\u002F08.payments\u002F10.refunds-disputes",{"title":1104,"path":1105,"stem":1106},"Тестування платіжних інтеграцій","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Ftesting-payments","01.csharp\u002F11.aspnet\u002F08.payments\u002F11.testing-payments",{"title":1108,"path":1109,"stem":1110},"Чекліст виходу в Production","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fproduction-checklist","01.csharp\u002F11.aspnet\u002F08.payments\u002F12.production-checklist",{"title":1112,"icon":1113,"items":1114,"path":1127,"stem":1128,"children":1129,"page":59},"Популярні бібліотеки","lucide:box",[1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126],"01.fluent-validation","02.mapster","03.erroror-result-pattern","04.serilog","05.mediatr","06.polly","07.health-checks","08.feature-management","09.fluent-email","10.quest-pdf","11.bogus","12.humanizer-guard","\u002Fcsharp\u002Faspnet\u002Flibraries","01.csharp\u002F11.aspnet\u002F09.libraries",[1130,1134,1138,1142,1146,1150,1154,1158,1162,1166,1170,1174,1178],{"title":1131,"path":1132,"stem":1133},"Валідація з FluentValidation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-validation","01.csharp\u002F11.aspnet\u002F09.libraries\u002F01.fluent-validation",{"title":1135,"path":1136,"stem":1137},"Маппінг об","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmapster","01.csharp\u002F11.aspnet\u002F09.libraries\u002F02.mapster",{"title":1139,"path":1140,"stem":1141},"Обробка помилок з ErrorOr та Result Pattern в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ferroror-result-pattern","01.csharp\u002F11.aspnet\u002F09.libraries\u002F03.erroror-result-pattern",{"title":1143,"path":1144,"stem":1145},"Структуроване логування з Serilog в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fserilog","01.csharp\u002F11.aspnet\u002F09.libraries\u002F04.serilog",{"title":1147,"path":1148,"stem":1149},"CQRS та Mediator з MediatR в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmediatr","01.csharp\u002F11.aspnet\u002F09.libraries\u002F05.mediatr",{"title":1151,"path":1152,"stem":1153},"Відмовостійкість з Polly в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fpolly","01.csharp\u002F11.aspnet\u002F09.libraries\u002F06.polly",{"title":1155,"path":1156,"stem":1157},"Health Checks в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F09.libraries\u002F07.health-checks",{"title":1159,"path":1160,"stem":1161},"Feature Management та Feature Flags в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffeature-management","01.csharp\u002F11.aspnet\u002F09.libraries\u002F08.feature-management",{"title":1163,"path":1164,"stem":1165},"Відправка Email з FluentEmail в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-email","01.csharp\u002F11.aspnet\u002F09.libraries\u002F09.fluent-email",{"title":1167,"path":1168,"stem":1169},"Генерація PDF з QuestPDF в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fquest-pdf","01.csharp\u002F11.aspnet\u002F09.libraries\u002F10.quest-pdf",{"title":1171,"path":1172,"stem":1173},"Генерація тестових даних з Bogus в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fbogus","01.csharp\u002F11.aspnet\u002F09.libraries\u002F11.bogus",{"title":1175,"path":1176,"stem":1177},"Humanizer та Guard Clauses в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhumanizer-guard","01.csharp\u002F11.aspnet\u002F09.libraries\u002F12.humanizer-guard",{"title":1179,"path":1180,"stem":1181},"План модуля 10.libraries — Популярні бібліотеки ASP.NET","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fplan","01.csharp\u002F11.aspnet\u002F09.libraries\u002Fplan",{"title":1183,"icon":1184,"path":1185,"stem":1186,"children":1187,"page":59},"Razor Pages","i-lucide-layout-template","\u002Fcsharp\u002Faspnet\u002Frazor-pages","01.csharp\u002F11.aspnet\u002F10.razor-pages",[1188,1192,1196,1200,1204,1208],{"title":1189,"path":1190,"stem":1191},"Від Minimal API до Razor Pages: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ffrom-minimal-api","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F01.from-minimal-api",{"title":1193,"path":1194,"stem":1195},"PageModel: логіка сторінки Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fpage-model","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F02.page-model",{"title":1197,"path":1198,"stem":1199},"Razor синтаксис: шаблонізатор у .cshtml","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Frazor-syntax","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F03.razor-syntax",{"title":1201,"path":1202,"stem":1203},"Tag Helpers: типізований HTML","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ftag-helpers","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F04.tag-helpers",{"title":1205,"path":1206,"stem":1207},"Форми і валідація: повний цикл обробки даних","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fforms-validation","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F05.forms-validation",{"title":1209,"path":1210,"stem":1211},"Практичний проєкт: TaskManager на Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fproject-task-manager","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F06.project-task-manager",{"title":1213,"path":1214,"stem":1215,"children":1216,"page":59},"ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc","01.csharp\u002F11.aspnet\u002F11.mvc",[1217,1221,1225,1229,1233,1237,1241,1245,1249,1253,1257,1261,1265,1269,1273,1277,1281,1285],{"title":1218,"path":1219,"stem":1220},"Патерн MVC: архітектура, що змінила веб","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-pattern","01.csharp\u002F11.aspnet\u002F11.mvc\u002F01.mvc-pattern",{"title":1222,"path":1223,"stem":1224},"Від Razor Pages до MVC: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffrom-razor-pages","01.csharp\u002F11.aspnet\u002F11.mvc\u002F02.from-razor-pages",{"title":1226,"path":1227,"stem":1228},"Controllers та Actions: серце MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fcontrollers-actions","01.csharp\u002F11.aspnet\u002F11.mvc\u002F03.controllers-actions",{"title":1230,"path":1231,"stem":1232},"Маршрутизація в MVC: Convention vs Attribute Routing","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Frouting-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F04.routing-mvc",{"title":1234,"path":1235,"stem":1236},"Model Binding: від HTTP до C#","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmodel-binding","01.csharp\u002F11.aspnet\u002F11.mvc\u002F05.model-binding",{"title":1238,"path":1239,"stem":1240},"Views, ViewData, ViewBag, TempData і ViewModel","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fviews-viewdata-tempdata","01.csharp\u002F11.aspnet\u002F11.mvc\u002F06.views-viewdata-tempdata",{"title":1242,"path":1243,"stem":1244},"Filters: аспектно-орієнтоване програмування в MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffilters","01.csharp\u002F11.aspnet\u002F11.mvc\u002F07.filters",{"title":1246,"path":1247,"stem":1248},"Areas: структурування великих застосунків","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fareas","01.csharp\u002F11.aspnet\u002F11.mvc\u002F08.areas",{"title":1250,"path":1251,"stem":1252},"View Components: повторювані незалежні блоки UI","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fview-components","01.csharp\u002F11.aspnet\u002F11.mvc\u002F09.view-components",{"title":1254,"path":1255,"stem":1256},"Display та Editor Templates","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fdisplay-editor-templates","01.csharp\u002F11.aspnet\u002F11.mvc\u002F10.display-editor-templates",{"title":1258,"path":1259,"stem":1260},"Валідація: IValidatableObject та FluentValidation","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fvalidation-advanced","01.csharp\u002F11.aspnet\u002F11.mvc\u002F11.validation-advanced",{"title":1262,"path":1263,"stem":1264},"HTMX: інтерактивність через HTML-атрибути","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx","01.csharp\u002F11.aspnet\u002F11.mvc\u002F12.htmx",{"title":1266,"path":1267,"stem":1268},"HTMX у ASP.NET Core MVC: серверна інтеграція","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fajax-htmx-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F13.ajax-htmx-mvc",{"title":1270,"path":1271,"stem":1272},"Практичний проєкт: Каталог товарів з HTMX","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F14.htmx-project",{"title":1274,"path":1275,"stem":1276},"Завантаження та обробка файлів","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffile-upload","01.csharp\u002F11.aspnet\u002F11.mvc\u002F15.file-upload",{"title":1278,"path":1279,"stem":1280},"Глобалізація та Локалізація MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fglobalization-localization","01.csharp\u002F11.aspnet\u002F11.mvc\u002F16.globalization-localization",{"title":1282,"path":1283,"stem":1284},"Підсумковий проєкт: Блог-платформа","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F17.mvc-project",{"title":1286,"path":1287,"stem":1288},"План курсу: ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fplan","01.csharp\u002F11.aspnet\u002F11.mvc\u002Fplan",{"title":1290,"path":1291,"stem":1292,"children":1293,"page":59},"Web Api","\u002Fcsharp\u002Faspnet\u002Fweb-api","01.csharp\u002F11.aspnet\u002F12.web-api",[1294,1298,1302,1306,1310,1314,1318,1322,1326,1330,1334,1338,1342],{"title":1295,"path":1296,"stem":1297},"Від Minimal API до Controller-based API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffrom-minimal-api-to-controllers","01.csharp\u002F11.aspnet\u002F12.web-api\u002F01.from-minimal-api-to-controllers",{"title":1299,"path":1300,"stem":1301},"ControllerBase, ActionResult\u003CT> та Response Types","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontroller-base-actionresult","01.csharp\u002F11.aspnet\u002F12.web-api\u002F02.controller-base-actionresult",{"title":1303,"path":1304,"stem":1305},"Content Negotiation - JSON, XML та власні форматери","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontent-negotiation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F03.content-negotiation",{"title":1307,"path":1308,"stem":1309},"Версіонування API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-versioning","01.csharp\u002F11.aspnet\u002F12.web-api\u002F04.api-versioning",{"title":1311,"path":1312,"stem":1313},"ProblemDetails та структурована обробка помилок","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fproblemdetails-error-handling","01.csharp\u002F11.aspnet\u002F12.web-api\u002F05.problemdetails-error-handling",{"title":1315,"path":1316,"stem":1317},"Фільтри у Web API контексті","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffilters-for-api","01.csharp\u002F11.aspnet\u002F12.web-api\u002F06.filters-for-api",{"title":1319,"path":1320,"stem":1321},"Пагінація, фільтрація та сортування","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fpagination-filtering-sorting","01.csharp\u002F11.aspnet\u002F12.web-api\u002F07.pagination-filtering-sorting",{"title":1323,"path":1324,"stem":1325},"HATEOAS та Resource Expansion","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhateoas-resource-expansion","01.csharp\u002F11.aspnet\u002F12.web-api\u002F08.hateoas-resource-expansion",{"title":1327,"path":1328,"stem":1329},"Гібридна архітектура - Minimal API + Controllers","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fminimal-api-vs-controllers-hybrid","01.csharp\u002F11.aspnet\u002F12.web-api\u002F09.minimal-api-vs-controllers-hybrid",{"title":1331,"path":1332,"stem":1333},"Документація API - Swashbuckle, NSwag та генерація клієнтів","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-documentation-generation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F10.api-documentation-generation",{"title":1335,"path":1336,"stem":1337},"Health Checks та моніторинг API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhealth-checks-monitoring","01.csharp\u002F11.aspnet\u002F12.web-api\u002F11.health-checks-monitoring",{"title":1339,"path":1340,"stem":1341},"Підсумковий проєкт - Production-Ready REST API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fweb-api-project","01.csharp\u002F11.aspnet\u002F12.web-api\u002F12.web-api-project",{"title":1343,"path":1344,"stem":1345},"План курсу: ASP.NET Core Web API (Controllers)","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fplan","01.csharp\u002F11.aspnet\u002F12.web-api\u002Fplan",{"title":1347,"icon":1348,"path":1349,"stem":1350,"children":1351,"page":59},"Моніторинг","i-lucide-activity","\u002Fcsharp\u002Faspnet\u002Fmonitoring","01.csharp\u002F11.aspnet\u002F13.monitoring",[1352,1356,1360],{"title":1353,"path":1354,"stem":1355},"Спостережуваність: від console.log до production-систем","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fobservability-intro","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F01.observability-intro",{"title":1357,"path":1358,"stem":1359},"Health Checks: перший рівень observability","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F02.health-checks",{"title":1361,"path":1362,"stem":1363},"Вбудовані метрики .NET 10 та System.Diagnostics.Metrics","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fdotnet-metrics","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F03.dotnet-metrics",{"title":1365,"icon":1366,"path":1367,"stem":1368,"children":1369,"page":59},"Desktop UI","i-lucide-app-window","\u002Fcsharp\u002Fdesktop-ui","01.csharp\u002F12.desktop-ui",[1370,1374,1378,1382,1386,1390,1394,1398,1402,1406,1410,1414,1418,1422,1426,1430,1434,1438,1442,1446,1450,1454,1458,1462,1466,1470,1474,1478,1482,1486,1490,1494,1498,1502,1506,1510,1514,1518,1522,1526,1530,1534,1538,1542,1546,1550,1554,1558,1562,1566,1570,1574,1578,1582,1586,1590,1594,1598,1602,1606,1610,1614,1618,1622,1626,1630,1634,1638,1642,1646,1650],{"title":1371,"path":1372,"stem":1373},"Що таке десктопна розробка?","\u002Fcsharp\u002Fdesktop-ui\u002Fwhat-is-desktop-dev","01.csharp\u002F12.desktop-ui\u002F01.what-is-desktop-dev",{"title":1375,"path":1376,"stem":1377},"Архітектура WPF — як влаштований графічний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-architecture","01.csharp\u002F12.desktop-ui\u002F02.wpf-architecture",{"title":1379,"path":1380,"stem":1381},"Перший WPF-проєкт — від нуля до вікна","\u002Fcsharp\u002Fdesktop-ui\u002Ffirst-wpf-app","01.csharp\u002F12.desktop-ui\u002F03.first-wpf-app",{"title":1383,"path":1384,"stem":1385},"Перший Avalonia-проєкт: WPF для всіх платформ","\u002Fcsharp\u002Fdesktop-ui\u002F03a.first-avalonia-app","01.csharp\u002F12.desktop-ui\u002F03a.first-avalonia-app",{"title":1387,"path":1388,"stem":1389},"XAML: декларативний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-basics","01.csharp\u002F12.desktop-ui\u002F04.xaml-basics",{"title":1391,"path":1392,"stem":1393},"Fluent UI у WPF — сучасний дизайн Windows 11","\u002Fcsharp\u002Fdesktop-ui\u002F04a.wpf-fluent-ui","01.csharp\u002F12.desktop-ui\u002F04a.wpf-fluent-ui",{"title":1395,"path":1396,"stem":1397},"WPF UI — сучасна бібліотека Fluent контролів","\u002Fcsharp\u002Fdesktop-ui\u002F04b.wpf-ui-library","01.csharp\u002F12.desktop-ui\u002F04b.wpf-ui-library",{"title":1399,"path":1400,"stem":1401},"HandyControl — велика бібліотека UI контролів для WPF","\u002Fcsharp\u002Fdesktop-ui\u002F04c.handycontrol-library","01.csharp\u002F12.desktop-ui\u002F04c.handycontrol-library",{"title":1403,"path":1404,"stem":1405},"Простори імен та ресурси XAML","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-namespaces-resources","01.csharp\u002F12.desktop-ui\u002F05.xaml-namespaces-resources",{"title":1407,"path":1408,"stem":1409},"XAML в Avalonia: ключові відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F05a.avalonia-xaml-differences","01.csharp\u002F12.desktop-ui\u002F05a.avalonia-xaml-differences",{"title":1411,"path":1412,"stem":1413},"Розширення розмітки XAML (Markup Extensions)","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-markup-extensions","01.csharp\u002F12.desktop-ui\u002F06.xaml-markup-extensions",{"title":1415,"path":1416,"stem":1417},"Панелі Layout: StackPanel, WrapPanel, DockPanel","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part1","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part1",{"title":1419,"path":1420,"stem":1421},"Grid, Canvas, UniformGrid","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part2","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part2",{"title":1423,"path":1424,"stem":1425},"Просунуті техніки Layout","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-advanced","01.csharp\u002F12.desktop-ui\u002F08.layout-advanced",{"title":1427,"path":1428,"stem":1429},"Адаптивний Layout та найкращі практики","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-responsive","01.csharp\u002F12.desktop-ui\u002F09.layout-responsive",{"title":1431,"path":1432,"stem":1433},"Layout в Avalonia: відмінності та нові можливості","\u002Fcsharp\u002Fdesktop-ui\u002F09a.layout-avalonia","01.csharp\u002F12.desktop-ui\u002F09a.layout-avalonia",{"title":1435,"path":1436,"stem":1437},"Button, Image, ProgressBar та інші базові контроли","\u002Fcsharp\u002Fdesktop-ui\u002Fbasic-controls","01.csharp\u002F12.desktop-ui\u002F10.basic-controls",{"title":1439,"path":1440,"stem":1441},"Контроли в Avalonia: відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F10a.controls-avalonia","01.csharp\u002F12.desktop-ui\u002F10a.controls-avalonia",{"title":1443,"path":1444,"stem":1445},"Текстові контроли — TextBlock, TextBox, RichTextBox","\u002Fcsharp\u002Fdesktop-ui\u002Ftext-controls","01.csharp\u002F12.desktop-ui\u002F11.text-controls",{"title":1447,"path":1448,"stem":1449},"Контроли вибору — CheckBox, RadioButton, ComboBox, ListBox, DatePicker","\u002Fcsharp\u002Fdesktop-ui\u002Fselection-controls","01.csharp\u002F12.desktop-ui\u002F12.selection-controls",{"title":1451,"path":1452,"stem":1453},"Content Model — GroupBox, Expander, TabControl, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fcontent-controls","01.csharp\u002F12.desktop-ui\u002F13.content-controls",{"title":1455,"path":1456,"stem":1457},"UI\u002FUX принципи десктопних застосунків","\u002Fcsharp\u002Fdesktop-ui\u002F13a.ui-ux-principles","01.csharp\u002F12.desktop-ui\u002F13a.ui-ux-principles",{"title":1459,"path":1460,"stem":1461},"Dependency Properties — Концепція та Value Resolution","\u002Fcsharp\u002Fdesktop-ui\u002Fdependency-properties-part1","01.csharp\u002F12.desktop-ui\u002F14.dependency-properties-part1",{"title":1463,"path":1464,"stem":1465},"Avalonia Property System — StyledProperty та DirectProperty","\u002Fcsharp\u002Fdesktop-ui\u002F14a.avalonia-property-system","01.csharp\u002F12.desktop-ui\u002F14a.avalonia-property-system",{"title":1467,"path":1468,"stem":1469},"Attached Properties — Властивості без меж","\u002Fcsharp\u002Fdesktop-ui\u002Fattached-properties","01.csharp\u002F12.desktop-ui\u002F15.attached-properties",{"title":1471,"path":1472,"stem":1473},"Routed Events — Маршрутизація подій у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Frouted-events","01.csharp\u002F12.desktop-ui\u002F16.routed-events",{"title":1475,"path":1476,"stem":1477},"Data Binding — Від Code-Behind до Декларативності","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part1","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part1",{"title":1479,"path":1480,"stem":1481},"INotifyPropertyChanged — Живе оновлення UI","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part2","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part2",{"title":1483,"path":1484,"stem":1485},"Compiled Bindings в Avalonia — Безпека на етапі компіляції","\u002Fcsharp\u002Fdesktop-ui\u002F17a.avalonia-compiled-bindings","01.csharp\u002F12.desktop-ui\u002F17a.avalonia-compiled-bindings",{"title":1487,"path":1488,"stem":1489},"Просунутий Data Binding — ElementName, RelativeSource, MultiBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-advanced","01.csharp\u002F12.desktop-ui\u002F18.data-binding-advanced",{"title":1491,"path":1492,"stem":1493},"Value Converters — Перетворення типів даних у Data Binding","\u002Fcsharp\u002Fdesktop-ui\u002Fvalue-converters","01.csharp\u002F12.desktop-ui\u002F19.value-converters",{"title":1495,"path":1496,"stem":1497},"Data Templates — Візуалізація об'єктів у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-templates","01.csharp\u002F12.desktop-ui\u002F20.data-templates",{"title":1499,"path":1500,"stem":1501},"Collections Binding Part 1 — ObservableCollection та ItemsControl","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part1","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part1",{"title":1503,"path":1504,"stem":1505},"Collections Binding Part 2 — ICollectionView, Filtering, Sorting та Virtualization","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part2","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part2",{"title":1507,"path":1508,"stem":1509},"MVVM Pattern — Від Spaghetti Code до архітектури","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-pattern","01.csharp\u002F12.desktop-ui\u002F22.mvvm-pattern",{"title":1511,"path":1512,"stem":1513},"ViewModel Implementation — Від BaseViewModel до валідації","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-implementation","01.csharp\u002F12.desktop-ui\u002F23.viewmodel-implementation",{"title":1515,"path":1516,"stem":1517},"Commands — Від event handlers до декларативних команд","\u002Fcsharp\u002Fdesktop-ui\u002Fcommands","01.csharp\u002F12.desktop-ui\u002F24.commands",{"title":1519,"path":1520,"stem":1521},"MVVM Toolkit — MVVM без boilerplate через Source Generators","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-toolkit","01.csharp\u002F12.desktop-ui\u002F25.mvvm-toolkit",{"title":1523,"path":1524,"stem":1525},"Messenger Pattern — Комунікація між ViewModel без прямих посилань","\u002Fcsharp\u002Fdesktop-ui\u002Fmessenger-pattern","01.csharp\u002F12.desktop-ui\u002F26.messenger-pattern",{"title":1527,"path":1528,"stem":1529},"Стилі WPF — CSS для десктопу","\u002Fcsharp\u002Fdesktop-ui\u002Fstyles-basics","01.csharp\u002F12.desktop-ui\u002F27.styles-basics",{"title":1531,"path":1532,"stem":1533},"CSS-like стилі Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F27a.avalonia-css-styling","01.csharp\u002F12.desktop-ui\u002F27a.avalonia-css-styling",{"title":1535,"path":1536,"stem":1537},"Control Templates — Частина 1. Концепція та TemplateBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part1","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part1",{"title":1539,"path":1540,"stem":1541},"Control Templates — Частина 2. Named Parts та ContentPresenter","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part2","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part2",{"title":1543,"path":1544,"stem":1545},"Control Themes в Avalonia — нова ера стилізації","\u002Fcsharp\u002Fdesktop-ui\u002F28a.avalonia-control-themes","01.csharp\u002F12.desktop-ui\u002F28a.avalonia-control-themes",{"title":1547,"path":1548,"stem":1549},"Triggers та Visual State Manager у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Ftriggers-visual-states","01.csharp\u002F12.desktop-ui\u002F29.triggers-visual-states",{"title":1551,"path":1552,"stem":1553},"Pseudo-classes в Avalonia — замість WPF Triggers","\u002Fcsharp\u002Fdesktop-ui\u002F29a.avalonia-pseudo-classes","01.csharp\u002F12.desktop-ui\u002F29a.avalonia-pseudo-classes",{"title":1555,"path":1556,"stem":1557},"Теми та ресурсні словники у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fresources-themes","01.csharp\u002F12.desktop-ui\u002F30.resources-themes",{"title":1559,"path":1560,"stem":1561},"Avalonia Themes — Fluent Design та система тематизації","\u002Fcsharp\u002Fdesktop-ui\u002F30a.avalonia-themes-fluent","01.csharp\u002F12.desktop-ui\u002F30a.avalonia-themes-fluent",{"title":1563,"path":1564,"stem":1565},"Контроли колекцій — глибоке занурення","\u002Fcsharp\u002Fdesktop-ui\u002Fcollection-controls","01.csharp\u002F12.desktop-ui\u002F31.collection-controls",{"title":1567,"path":1568,"stem":1569},"DataGrid — колонки та базове відображення","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part1","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part1",{"title":1571,"path":1572,"stem":1573},"DataGrid — сортування, фільтрація, редагування","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part2","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part2",{"title":1575,"path":1576,"stem":1577},"TreeView та GridView","\u002Fcsharp\u002Fdesktop-ui\u002Ftreeview-listview","01.csharp\u002F12.desktop-ui\u002F33.treeview-listview",{"title":1579,"path":1580,"stem":1581},"Меню, Toolbar, ContextMenu, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fmenus-toolbars","01.csharp\u002F12.desktop-ui\u002F34.menus-toolbars",{"title":1583,"path":1584,"stem":1585},"Навігація та керування вікнами. Частина 1: вікна та сторінки","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part1","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part1",{"title":1587,"path":1588,"stem":1589},"Навігація та керування вікнами. Частина 2: MVVM-навігація","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part2","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part2",{"title":1591,"path":1592,"stem":1593},"Avalonia — Навігація та діалоги","\u002Fcsharp\u002Fdesktop-ui\u002F35a.avalonia-navigation-dialogs","01.csharp\u002F12.desktop-ui\u002F35a.avalonia-navigation-dialogs",{"title":1595,"path":1596,"stem":1597},"Діалоги та File Pickers у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdialogs-file-pickers","01.csharp\u002F12.desktop-ui\u002F36.dialogs-file-pickers",{"title":1599,"path":1600,"stem":1601},"UserControl: компонентний підхід у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fuser-controls","01.csharp\u002F12.desktop-ui\u002F37.user-controls",{"title":1603,"path":1604,"stem":1605},"Custom Controls: Lookless Controls у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fcustom-controls","01.csharp\u002F12.desktop-ui\u002F38.custom-controls",{"title":1607,"path":1608,"stem":1609},"Avalonia TemplatedControl — Lookless Controls","\u002Fcsharp\u002Fdesktop-ui\u002F38a.avalonia-templated-controls","01.csharp\u002F12.desktop-ui\u002F38a.avalonia-templated-controls",{"title":1611,"path":1612,"stem":1613},"Анімації у WPF: Storyboard та Easing Functions","\u002Fcsharp\u002Fdesktop-ui\u002Fanimations-transitions","01.csharp\u002F12.desktop-ui\u002F39.animations-transitions",{"title":1615,"path":1616,"stem":1617},"Анімації в Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F39a.avalonia-animations","01.csharp\u002F12.desktop-ui\u002F39a.avalonia-animations",{"title":1619,"path":1620,"stem":1621},"2D Графіка та Мультимедіа у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fmedia-graphics","01.csharp\u002F12.desktop-ui\u002F40.media-graphics",{"title":1623,"path":1624,"stem":1625},"Dependency Injection у WPF та Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Fdi-integration","01.csharp\u002F12.desktop-ui\u002F41.di-integration",{"title":1627,"path":1628,"stem":1629},"SQLite та EF Core у десктопних додатках","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part1","01.csharp\u002F12.desktop-ui\u002F42.data-persistence-part1",{"title":1631,"path":1632,"stem":1633},"Repository Pattern та Unit of Work","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part2","01.csharp\u002F12.desktop-ui\u002F43.data-persistence-part2",{"title":1635,"path":1636,"stem":1637},"Тестування ViewModels","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-testing","01.csharp\u002F12.desktop-ui\u002F44.viewmodel-testing",{"title":1639,"path":1640,"stem":1641},"Avalonia Headless Testing — тестування UI без вікон","\u002Fcsharp\u002Fdesktop-ui\u002F44a.avalonia-headless-testing","01.csharp\u002F12.desktop-ui\u002F44a.avalonia-headless-testing",{"title":1643,"path":1644,"stem":1645},"Кросплатформна розробка з Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-cross-platform","01.csharp\u002F12.desktop-ui\u002F45.avalonia-cross-platform",{"title":1647,"path":1648,"stem":1649},"Пакування та розгортання Avalonia додатків","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F46.avalonia-packaging-deployment",{"title":1651,"path":1652,"stem":1653},"Розгортання WPF застосунків","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F47.wpf-packaging-deployment",{"title":1655,"icon":658,"path":1656,"stem":1657,"children":1658,"page":59},"Network Programming","\u002Fcsharp\u002Fnetwork-programming","01.csharp\u002F13.network-programming",[1659,1663,1667,1671,1675,1679,1683,1687,1691,1695,1699],{"title":1660,"path":1661,"stem":1662},"Основи комп'ютерних мереж","\u002Fcsharp\u002Fnetwork-programming\u002Ffoundations","01.csharp\u002F13.network-programming\u002F01.foundations",{"title":1664,"path":1665,"stem":1666},"Модель OSI та стек TCP\u002FIP","\u002Fcsharp\u002Fnetwork-programming\u002Fosi-model","01.csharp\u002F13.network-programming\u002F02.osi-model",{"title":1668,"path":1669,"stem":1670},"IP-протокол та адресація","\u002Fcsharp\u002Fnetwork-programming\u002Fip-addressing","01.csharp\u002F13.network-programming\u002F03.ip-addressing",{"title":1672,"path":1673,"stem":1674},"UDP — протокол без з'єднання","\u002Fcsharp\u002Fnetwork-programming\u002Fudp","01.csharp\u002F13.network-programming\u002F05.udp",{"title":1676,"path":1677,"stem":1678},"UDP Broadcast та Multicast","\u002Fcsharp\u002Fnetwork-programming\u002Fudp-broadcast-multicast","01.csharp\u002F13.network-programming\u002F06.udp-broadcast-multicast",{"title":1680,"path":1681,"stem":1682},"HTTP — протокол вебу","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-fundamentals","01.csharp\u002F13.network-programming\u002F07.http-fundamentals",{"title":1684,"path":1685,"stem":1686},"HttpListener — вбудований HTTP-сервер .NET","\u002Fcsharp\u002Fnetwork-programming\u002F07a.http-listener","01.csharp\u002F13.network-programming\u002F07a.http-listener",{"title":1688,"path":1689,"stem":1690},"HTTP Advanced — cookies, аутентифікація та HTTPS","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-advanced","01.csharp\u002F13.network-programming\u002F08.http-advanced",{"title":1692,"path":1693,"stem":1694},"SMTP та протоколи електронної пошти","\u002Fcsharp\u002Fnetwork-programming\u002Fsmtp","01.csharp\u002F13.network-programming\u002F09.smtp",{"title":1696,"path":1697,"stem":1698},"WebSocket — повнодуплексний протокол реального часу","\u002Fcsharp\u002Fnetwork-programming\u002Fwebsockets","01.csharp\u002F13.network-programming\u002F10.websockets",{"title":1700,"path":1701,"stem":1702},"TLS\u002FSSL — криптографічний захист мережевих з'єднань","\u002Fcsharp\u002Fnetwork-programming\u002Ftls-ssl","01.csharp\u002F13.network-programming\u002F11.tls-ssl",{"title":1704,"path":1705,"stem":1706},"C# & .NET: The Ultimate Roadmap","\u002Fcsharp\u002Froadmap","01.csharp\u002Froadmap",{"title":1708,"icon":1709,"path":1710,"stem":1711,"children":1712,"page":59},"C++","i-devicon-cplusplus","\u002Fcpp","02.cpp",[1713,1717,1721,1725,1729,1733,1737,1741,1745,1748,1752,1756,1760,1764,1768,1772,1776,1780,1784,1788,1792,1796,1800,1804,1808,1812,1816,1820,1824,1828,1832,1836,1840,1844,1848,1852,1856,1860,1864,1868,1872,1876,1880,1884,1888,1892,1896,1900,1904,1908],{"title":1714,"path":1715,"stem":1716},"Вступ у програмування та алгоритми","\u002Fcpp\u002Fintro-algorithms","02.cpp\u002F01.intro-algorithms",{"title":1718,"path":1719,"stem":1720},"Code Style: угоди про оформлення коду","\u002Fcpp\u002Fcode-style","02.cpp\u002F02.code-style",{"title":1722,"path":1723,"stem":1724},"Середовище розробки та перший проєкт","\u002Fcpp\u002Fide-setup","02.cpp\u002F03.ide-setup",{"title":1726,"path":1727,"stem":1728},"Вивід даних на екран","\u002Fcpp\u002Fdata-output","02.cpp\u002F04.data-output",{"title":1730,"path":1731,"stem":1732},"Типи даних, змінні та константи","\u002Fcpp\u002Fdata-types-variables","02.cpp\u002F05.data-types-variables",{"title":1734,"path":1735,"stem":1736},"Ввід даних з клавіатури","\u002Fcpp\u002Fdata-input","02.cpp\u002F06.data-input",{"title":1738,"path":1739,"stem":1740},"Оператори, перетворення типів та логічні операції","\u002Fcpp\u002Foperators-type-conversion","02.cpp\u002F07.operators-type-conversion",{"title":1742,"path":1743,"stem":1744},"Цикли","\u002Fcpp\u002Floops","02.cpp\u002F08.loops",{"title":32,"path":1746,"stem":1747},"\u002Fcpp\u002Farrays","02.cpp\u002F09.arrays",{"title":1749,"path":1750,"stem":1751},"Алгоритми сортування та аналіз складності","\u002Fcpp\u002Fsorting","02.cpp\u002F10.sorting",{"title":1753,"path":1754,"stem":1755},"Алгоритми пошуку","\u002Fcpp\u002Fsearching","02.cpp\u002F11.searching",{"title":1757,"path":1758,"stem":1759},"Функції: основи","\u002Fcpp\u002Ffunctions-basics","02.cpp\u002F12.functions-basics",{"title":1761,"path":1762,"stem":1763},"Функції: прототипи, область видимості та додаткові можливості","\u002Fcpp\u002Ffunctions-scope","02.cpp\u002F13.functions-scope",{"title":1765,"path":1766,"stem":1767},"Функції: перевантаження та шаблони","\u002Fcpp\u002Ffunctions-overloading-templates","02.cpp\u002F14.functions-overloading-templates",{"title":1769,"path":1770,"stem":1771},"Вказівники: основи","\u002Fcpp\u002Fpointers-basics","02.cpp\u002F15.pointers-basics",{"title":1773,"path":1774,"stem":1775},"Посилання (References)","\u002Fcpp\u002Freferences","02.cpp\u002F16.references",{"title":1777,"path":1778,"stem":1779},"Вказівники, const і масиви","\u002Fcpp\u002Fpointers-const-arrays","02.cpp\u002F17.pointers-const-arrays",{"title":1781,"path":1782,"stem":1783},"Адресна арифметика","\u002Fcpp\u002Fpointer-arithmetic","02.cpp\u002F18.pointer-arithmetic",{"title":1785,"path":1786,"stem":1787},"Динамічна пам'ять","\u002Fcpp\u002Fdynamic-memory","02.cpp\u002F19.dynamic-memory",{"title":1789,"path":1790,"stem":1791},"Вказівники типу void","\u002Fcpp\u002Fvoid-pointers","02.cpp\u002F20.void-pointers",{"title":1793,"path":1794,"stem":1795},"Вказівники на вказівники","\u002Fcpp\u002Fpointers-to-pointers","02.cpp\u002F21.pointers-to-pointers",{"title":1797,"path":1798,"stem":1799},"Оператор доступу до членів через вказівник (->)","\u002Fcpp\u002Fmember-access-operator","02.cpp\u002F22.member-access-operator",{"title":1801,"path":1802,"stem":1803},"Цикл for-each (Range-based for)","\u002Fcpp\u002Fforeach-loop","02.cpp\u002F23.foreach-loop",{"title":1805,"path":1806,"stem":1807},"Вказівники на функції","\u002Fcpp\u002Ffunction-pointers","02.cpp\u002F24.function-pointers",{"title":1809,"path":1810,"stem":1811},"Лямбда-вирази","\u002Fcpp\u002Flambdas","02.cpp\u002F25.lambdas",{"title":1813,"path":1814,"stem":1815},"Лямбда-захоплення","\u002Fcpp\u002Flambda-captures","02.cpp\u002F26.lambda-captures",{"title":1817,"path":1818,"stem":1819},"Еліпсис","\u002Fcpp\u002Fellipsis","02.cpp\u002F27.ellipsis",{"title":1821,"path":1822,"stem":1823},"Безпечні альтернативи еліпсису","\u002Fcpp\u002F27a.ellipsis","02.cpp\u002F27a.ellipsis",{"title":1825,"path":1826,"stem":1827},"Аргументи командного рядка","\u002Fcpp\u002Fcommand-line-arguments","02.cpp\u002F28.command-line-arguments",{"title":1829,"path":1830,"stem":1831},"Перерахування (enum)","\u002Fcpp\u002Fenum","02.cpp\u002F29.enum",{"title":1833,"path":1834,"stem":1835},"Класи-перерахування (enum class)","\u002Fcpp\u002Fenum-class","02.cpp\u002F30.enum-class",{"title":1837,"path":1838,"stem":1839},"Псевдоніми типів (typedef і using)","\u002Fcpp\u002Ftype-aliases","02.cpp\u002F31.type-aliases",{"title":1841,"path":1842,"stem":1843},"Системи числення та двійкова арифметика","\u002Fcpp\u002Fnumber-systems","02.cpp\u002F32.number-systems",{"title":1845,"path":1846,"stem":1847},"Структури (struct): агрегування даних","\u002Fcpp\u002Fstruct","02.cpp\u002F33.struct",{"title":1849,"path":1850,"stem":1851},"Структури у функціях","\u002Fcpp\u002Fstruct-functions","02.cpp\u002F34.struct-functions",{"title":1853,"path":1854,"stem":1855},"Масиви структур і вкладені структури","\u002Fcpp\u002Fstruct-arrays","02.cpp\u002F35.struct-arrays",{"title":1857,"path":1858,"stem":1859},"Патерни struct та межі застосування","\u002Fcpp\u002Fstruct-patterns","02.cpp\u002F36.struct-patterns",{"title":1861,"path":1862,"stem":1863},"Символи та таблиця ASCII","\u002Fcpp\u002Fascii-characters","02.cpp\u002F37.ascii-characters",{"title":1865,"path":1866,"stem":1867},"Unicode та кодування UTF","\u002Fcpp\u002Funicode-utf","02.cpp\u002F38.unicode-utf",{"title":1869,"path":1870,"stem":1871},"C-style рядки","\u002Fcpp\u002Fc-strings","02.cpp\u002F39.c-strings",{"title":1873,"path":1874,"stem":1875},"Вступ до std::string","\u002Fcpp\u002Fstd-string-intro","02.cpp\u002F40.std-string-intro",{"title":1877,"path":1878,"stem":1879},"Довжина, ємність та доступ до символів std::string","\u002Fcpp\u002Fstd-string-capacity-access","02.cpp\u002F41.std-string-capacity-access",{"title":1881,"path":1882,"stem":1883},"Модифікація std::string: присвоювання, додавання, вставка, видалення та заміна","\u002Fcpp\u002Fstd-string-modification","02.cpp\u002F42.std-string-modification",{"title":1885,"path":1886,"stem":1887},"Пошук у std::string: find, npos та практичні патерни","\u002Fcpp\u002Fstd-string-search","02.cpp\u002F43.std-string-search",{"title":1889,"path":1890,"stem":1891},"std::string_view: невласницький погляд на рядок без копіювання","\u002Fcpp\u002Fstd-string-view","02.cpp\u002F44.std-string-view",{"title":1893,"path":1894,"stem":1895},"Об'єднання (union): один блок пам'яті, кілька інтерпретацій","\u002Fcpp\u002Funion","02.cpp\u002F45.union",{"title":1897,"path":1898,"stem":1899},"Організація коду: файли, препроцесор, простори імен","\u002Fcpp\u002Fmultifile-programs","02.cpp\u002F46.multifile-programs",{"title":1901,"path":1902,"stem":1903},"Робота з файлами: C-стиль (stdio.h)","\u002Fcpp\u002Fc-style-files","02.cpp\u002F47.c-style-files",{"title":1905,"path":1906,"stem":1907},"Робота з файлами: C++-стиль (fstream)","\u002Fcpp\u002Fcpp-style-files","02.cpp\u002F48.cpp-style-files",{"title":1909,"path":1910,"stem":1911},"План навчання: Курс C++ — Продовження (Статті 29–60+)","\u002Fcpp\u002Fcurriculum-plan","02.cpp\u002Fcurriculum-plan",{"title":1913,"icon":1914,"path":1915,"stem":1916,"children":1917,"page":59},"JavaScript","i-devicon-javascript","\u002Fjavascript","03.javascript",[1918,1944,1998,2020,2324,2362],{"title":1919,"icon":1920,"path":1921,"stem":1922,"children":1923,"page":59},"Events","i-lucide-mouse-pointer-click","\u002Fjavascript\u002Fevents","03.javascript\u002F01.events",[1924,1928,1932,1936,1940],{"title":1925,"path":1926,"stem":1927},"Вступ до подій браузера","\u002Fjavascript\u002Fevents\u002Fintro","03.javascript\u002F01.events\u002F01.intro",{"title":1929,"path":1930,"stem":1931},"Бульбашковий механізм (Bubbling) та занурення (Capturing)","\u002Fjavascript\u002Fevents\u002Fbubbling-capturing","03.javascript\u002F01.events\u002F02.bubbling-capturing",{"title":1933,"path":1934,"stem":1935},"Делегування подій (Event Delegation)","\u002Fjavascript\u002Fevents\u002Fdelegate-events","03.javascript\u002F01.events\u002F03.delegate-events",{"title":1937,"path":1938,"stem":1939},"Типові дії браузера та preventDefault()","\u002Fjavascript\u002Fevents\u002Fprevent-default","03.javascript\u002F01.events\u002F04.prevent-default",{"title":1941,"path":1942,"stem":1943},"Запуск користувацьких подій (Custom Events)","\u002Fjavascript\u002Fevents\u002Fcustom-events","03.javascript\u002F01.events\u002F05.custom-events",{"title":1945,"icon":1946,"path":1947,"stem":1948,"children":1949,"page":59},"Network","i-lucide-globe","\u002Fjavascript\u002Fnetwork","03.javascript\u002F02.network",[1950,1954,1958,1962,1966,1970,1974,1978,1982,1986,1990,1994],{"title":1951,"path":1952,"stem":1953},"Fetch API - Сучасний підхід до HTTP-запитів","\u002Fjavascript\u002Fnetwork\u002F01-fetch-api","03.javascript\u002F02.network\u002F01-fetch-api",{"title":1955,"path":1956,"stem":1957},"FormData - Робота з формами та файлами","\u002Fjavascript\u002Fnetwork\u002F02-formdata","03.javascript\u002F02.network\u002F02-formdata",{"title":1959,"path":1960,"stem":1961},"Відстеження прогресу завантаження","\u002Fjavascript\u002Fnetwork\u002F03-download-progress","03.javascript\u002F02.network\u002F03-download-progress",{"title":1963,"path":1964,"stem":1965},"Переривання fetch-запитів","\u002Fjavascript\u002Fnetwork\u002F04-abort-requests","03.javascript\u002F02.network\u002F04-abort-requests",{"title":1967,"path":1968,"stem":1969},"CORS - Запити між різними джерелами","\u002Fjavascript\u002Fnetwork\u002F05-cors","03.javascript\u002F02.network\u002F05-cors",{"title":1971,"path":1972,"stem":1973},"Fetch API - Повний довідник опцій","\u002Fjavascript\u002Fnetwork\u002F06-fetch-options","03.javascript\u002F02.network\u002F06-fetch-options",{"title":1975,"path":1976,"stem":1977},"URL Objects - Робота з посиланнями","\u002Fjavascript\u002Fnetwork\u002F07-url-objects","03.javascript\u002F02.network\u002F07-url-objects",{"title":1979,"path":1980,"stem":1981},"XMLHttpRequest - AJAX та низькорівневі запити","\u002Fjavascript\u002Fnetwork\u002F08-xmlhttprequest","03.javascript\u002F02.network\u002F08-xmlhttprequest",{"title":1983,"path":1984,"stem":1985},"Відновлюване завантаження файлів","\u002Fjavascript\u002Fnetwork\u002F09-resumable-upload","03.javascript\u002F02.network\u002F09-resumable-upload",{"title":1987,"path":1988,"stem":1989},"Cookies, document.cookie та світ після \"Cookiepocalypse\"","\u002Fjavascript\u002Fnetwork\u002F10-cookies","03.javascript\u002F02.network\u002F10-cookies",{"title":1991,"path":1992,"stem":1993},"js-cookie: Керування Cookies без Болю","\u002Fjavascript\u002Fnetwork\u002F11-js-cookie","03.javascript\u002F02.network\u002F11-js-cookie",{"title":1995,"path":1996,"stem":1997},"Axios: Потужний HTTP-клієнт для JavaScript","\u002Fjavascript\u002Fnetwork\u002F12-axios","03.javascript\u002F02.network\u002F12-axios",{"title":1999,"icon":2000,"path":2001,"stem":2002,"children":2003,"page":59},"Bom","i-lucide-monitor","\u002Fjavascript\u002Fbom","03.javascript\u002F03.bom",[2004,2008,2012,2016],{"title":2005,"path":2006,"stem":2007},"LocalStorage, SessionStorage та patterns збереження даних","\u002Fjavascript\u002Fbom\u002F01-localstorage","03.javascript\u002F03.bom\u002F01-localstorage",{"title":2009,"path":2010,"stem":2011},"Location Object - Керування адресою сторінки","\u002Fjavascript\u002Fbom\u002F02-location-object","03.javascript\u002F03.bom\u002F02-location-object",{"title":2013,"path":2014,"stem":2015},"History API - Керування історією браузера","\u002Fjavascript\u002Fbom\u002F03-history-api","03.javascript\u002F03.bom\u002F03-history-api",{"title":2017,"path":2018,"stem":2019},"Navigator Object - Ідентифікація та Можливості Пристрою","\u002Fjavascript\u002Fbom\u002F04-navigator-object","03.javascript\u002F03.bom\u002F04-navigator-object",{"title":2021,"icon":2022,"path":2023,"stem":2024,"children":2025},"React","i-devicon-react","\u002Fjavascript\u002Freact","03.javascript\u002F04.react\u002Findex",[2026,2027,2031,2035,2039,2043,2106,2141,2293],{"title":2021,"path":2023,"stem":2024},{"title":2028,"path":2029,"stem":2030},"Робота з Формами в React","\u002Fjavascript\u002Freact\u002Freact-forms","03.javascript\u002F04.react\u002F01.react-forms",{"title":2032,"path":2033,"stem":2034},"React Hook Form: Професійна Робота з Формами","\u002Fjavascript\u002Freact\u002Freact-hook-form","03.javascript\u002F04.react\u002F02.react-hook-form",{"title":2036,"path":2037,"stem":2038},"React Hook Form: Глибоке Розуміння Архітектури та Оптимізації","\u002Fjavascript\u002Freact\u002Freact-hook-form-new","03.javascript\u002F04.react\u002F02.react-hook-form-new",{"title":2040,"path":2041,"stem":2042},"Axios та React: Професійна Архітектура Запитів","\u002Fjavascript\u002Freact\u002Fdata-fetching-axios","03.javascript\u002F04.react\u002F03.data-fetching-axios",{"title":2044,"icon":132,"path":2045,"stem":2046,"children":2047},"Tanstack Query","\u002Fjavascript\u002Freact\u002Ftanstack-query","03.javascript\u002F04.react\u002F04.tanstack-query\u002Findex",[2048,2050,2054,2058,2062,2066,2070,2074,2078,2082,2086,2090,2094,2098,2102],{"title":2049,"path":2045,"stem":2046},"TanStack Query: Майстерність Керування Станом Сервера",{"title":2051,"path":2052,"stem":2053},"Парадигма Server State: Чому useEffect недостатньо","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-state-paradigm","03.javascript\u002F04.react\u002F04.tanstack-query\u002F01.server-state-paradigm",{"title":2055,"path":2056,"stem":2057},"Встановлення та Налаштування: Фундамент","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Finstallation-and-devtools","03.javascript\u002F04.react\u002F04.tanstack-query\u002F02.installation-and-devtools",{"title":2059,"path":2060,"stem":2061},"Основи Запитів та Магія Ключів","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fquery-basics-and-keys","03.javascript\u002F04.react\u002F04.tanstack-query\u002F03.query-basics-and-keys",{"title":2063,"path":2064,"stem":2065},"Синхронізація Даних: Життєвий Цикл Запиту","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fdata-synchronization","03.javascript\u002F04.react\u002F04.tanstack-query\u002F04.data-synchronization",{"title":2067,"path":2068,"stem":2069},"Мутації та Інвалідація: Зміна Даних","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fmutations-and-invalidation","03.javascript\u002F04.react\u002F04.tanstack-query\u002F05.mutations-and-invalidation",{"title":2071,"path":2072,"stem":2073},"Оптимістичні Оновлення: Швидше за Світло","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Foptimistic-updates","03.javascript\u002F04.react\u002F04.tanstack-query\u002F06.optimistic-updates",{"title":2075,"path":2076,"stem":2077},"Пагінація та Infinite Scroll","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fpagination-and-load-more","03.javascript\u002F04.react\u002F04.tanstack-query\u002F07.pagination-and-load-more",{"title":2079,"path":2080,"stem":2081},"Просунуті Патерни та Оптимізація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F04.tanstack-query\u002F08.advanced-patterns",{"title":2083,"path":2084,"stem":2085},"Архітектура та Best Practices","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Farchitecture-and-best-practices","03.javascript\u002F04.react\u002F04.tanstack-query\u002F09.architecture-and-best-practices",{"title":2087,"path":2088,"stem":2089},"Server-Side Rendering (SSR) та Гідратація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-side-rendering","03.javascript\u002F04.react\u002F04.tanstack-query\u002F10.server-side-rendering",{"title":2091,"path":2092,"stem":2093},"Стратегії Тестування","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Ftesting-strategies","03.javascript\u002F04.react\u002F04.tanstack-query\u002F11.testing-strategies",{"title":2095,"path":2096,"stem":2097},"Аутентифікація та Обробка Помилок","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fauthentication-and-errors","03.javascript\u002F04.react\u002F04.tanstack-query\u002F12.authentication-and-errors",{"title":2099,"path":2100,"stem":2101},"React Suspense та Майбутнє","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Freact-suspense","03.javascript\u002F04.react\u002F04.tanstack-query\u002F13.react-suspense",{"title":2103,"path":2104,"stem":2105},"Глибоке Занурення в Продуктивність","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fperformance-deep-dive","03.javascript\u002F04.react\u002F04.tanstack-query\u002F14.performance-deep-dive",{"title":2107,"icon":2022,"path":2108,"stem":2109,"children":2110},"React Router","\u002Fjavascript\u002Freact\u002Freact-router","03.javascript\u002F04.react\u002F05.react-router\u002Findex",[2111,2113,2117,2121,2125,2129,2133,2137],{"title":2112,"path":2108,"stem":2109},"React Router: Навігаційна система сучасного вебу",{"title":2114,"path":2115,"stem":2116},"Налаштування та Базовий Роутинг","\u002Fjavascript\u002Freact\u002Freact-router\u002Fsetup-and-basic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F01.setup-and-basic-routing",{"title":2118,"path":2119,"stem":2120},"Динамічна Навігація","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnavigation-and-links","03.javascript\u002F04.react\u002F05.react-router\u002F02.navigation-and-links",{"title":2122,"path":2123,"stem":2124},"Вкладені Маршрути та Макети","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnested-routes-and-layouts","03.javascript\u002F04.react\u002F05.react-router\u002F03.nested-routes-and-layouts",{"title":2126,"path":2127,"stem":2128},"Динамічні Маршрути та Параметри","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdynamic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F04.dynamic-routing",{"title":2130,"path":2131,"stem":2132},"Data APIs: Loaders та Actions","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdata-loading","03.javascript\u002F04.react\u002F05.react-router\u002F05.data-loading",{"title":2134,"path":2135,"stem":2136},"Просунуті Патерни","\u002Fjavascript\u002Freact\u002Freact-router\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F05.react-router\u002F06.advanced-patterns",{"title":2138,"path":2139,"stem":2140},"Legacy Routing: Компонентний підхід","\u002Fjavascript\u002Freact\u002Freact-router\u002Flegacy-routing","03.javascript\u002F04.react\u002F05.react-router\u002F07.legacy-routing",{"title":2142,"icon":132,"path":2143,"stem":2144,"children":2145},"Redux","\u002Fjavascript\u002Freact\u002Fredux","03.javascript\u002F04.react\u002F06.redux\u002Findex",[2146,2148,2164,2193,2202,2223,2239,2268],{"title":2147,"path":2143,"stem":2144},"Redux: Еволюція управління станом",{"title":14,"icon":15,"path":2149,"stem":2150,"children":2151,"page":59},"\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals",[2152,2156,2160],{"title":2153,"path":2154,"stem":2155},"Вступ до State Management","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fintro-state-management","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F01.intro-state-management",{"title":2157,"path":2158,"stem":2159},"Філософія Redux та Три Принципи","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fredux-philosophy","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F02.redux-philosophy",{"title":2161,"path":2162,"stem":2163},"Чисті функції та Іммутабельність","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fpure-functions-immutability","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F03.pure-functions-immutability",{"title":2165,"icon":132,"path":2166,"stem":2167,"children":2168,"page":59},"Classic Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux",[2169,2173,2177,2181,2185,2189],{"title":2170,"path":2171,"stem":2172},"Створення Store (Classic Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fstore-setup","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F01.store-setup",{"title":2174,"path":2175,"stem":2176},"Actions, Constants та Action Creators","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Factions-constants","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F02.actions-constants",{"title":2178,"path":2179,"stem":2180},"Логіка Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freducers","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F03.reducers",{"title":2182,"path":2183,"stem":2184},"Комбінування Reducers (Root Reducer)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fdata-flow","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F04.data-flow",{"title":2186,"path":2187,"stem":2188},"Підключення до React (React-Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freact-redux-connection","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F05.react-redux-connection",{"title":2190,"path":2191,"stem":2192},"Middleware та Асинхронність (Redux Thunk)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fmiddleware-thunk","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F06.middleware-thunk",{"title":2194,"icon":132,"path":2195,"stem":2196,"children":2197,"page":59},"Transition To Rtk","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk",[2198],{"title":2199,"path":2200,"stem":2201},"Проблеми класичного Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk\u002Fproblems-with-classic","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk\u002F01.problems-with-classic",{"title":2203,"icon":132,"path":2204,"stem":2205,"children":2206,"page":59},"Redux Toolkit","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit",[2207,2211,2215,2219],{"title":2208,"path":2209,"stem":2210},"Налаштування Store з configureStore","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fconfigure-store","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F01.configure-store",{"title":2212,"path":2213,"stem":2214},"createSlice: Революція в Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fcreate-slice","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F02.create-slice",{"title":2216,"path":2217,"stem":2218},"Асинхронність з createAsyncThunk","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fasync-thunks","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F03.async-thunks",{"title":2220,"path":2221,"stem":2222},"04. Entity Adapter: Керування нормалізованим станом","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fentity-adapter","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F04.entity-adapter",{"title":2224,"icon":92,"path":2225,"stem":2226,"children":2227,"page":59},"Advanced","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced",[2228,2232,2236],{"title":2229,"path":2230,"stem":2231},"Мемоізація та Селектори: Повний Гайд по Reselect","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Fselectors-reselect","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F01.selectors-reselect",{"title":2233,"path":2234,"stem":2235},"RTK Query: Архітектура Серверного Кешу","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Frtk-query-intro","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F02.rtk-query-intro",{"title":2083,"path":2237,"stem":2238},"\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Farchitecture-best-practices","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F03.architecture-best-practices",{"title":2240,"icon":132,"path":2241,"stem":2242,"children":2243,"page":59},"Project Kanban","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban",[2244,2248,2252,2256,2260,2264],{"title":2245,"path":2246,"stem":2247},"Проєкт: Kanban Board (Trello Clone)","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fproject-overview","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F01.project-overview",{"title":2249,"path":2250,"stem":2251},"Налаштування та Типізація","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fsetup-and-types","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F02.setup-and-types",{"title":2253,"path":2254,"stem":2255},"Board Slice: Серце Дошки","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fboard-slice","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F03.board-slice",{"title":2257,"path":2258,"stem":2259},"Логіка Drag & Drop","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fdrag-and-drop-logic","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F04.drag-and-drop-logic",{"title":2261,"path":2262,"stem":2263},"Інтеграція з RTK Query","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Frtk-query-integration","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F05.rtk-query-integration",{"title":2265,"path":2266,"stem":2267},"Optimistic Updates","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Foptimistic-updates","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F06.optimistic-updates",{"title":2269,"icon":132,"path":2270,"stem":2271,"children":2272,"page":59},"Testing","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting","03.javascript\u002F04.react\u002F06.redux\u002F07.testing",[2273,2277,2281,2285,2289],{"title":2274,"path":2275,"stem":2276},"Тестування Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Fintro-testing","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F01.intro-testing",{"title":2278,"path":2279,"stem":2280},"Тестування Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-reducers","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F02.testing-reducers",{"title":2282,"path":2283,"stem":2284},"Тестування Селекторів","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-selectors","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F03.testing-selectors",{"title":2286,"path":2287,"stem":2288},"Тестування Компонентів (Integration)","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-components","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F04.testing-components",{"title":2290,"path":2291,"stem":2292},"Тестування Async Thunks","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-thunks","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F05.testing-thunks",{"title":2294,"icon":132,"path":2295,"stem":2296,"children":2297},"Ui Libraries","\u002Fjavascript\u002Freact\u002Fui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002Findex",[2298,2300,2304,2308,2312,2316,2320],{"title":2299,"path":2295,"stem":2296},"UI Бібліотеки в React",{"title":2301,"path":2302,"stem":2303},"Вступ до UI Бібліотек: Навіщо Винаходити Велосипед Двічі?","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fintroduction-to-ui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002F01.introduction-to-ui-libraries",{"title":2305,"path":2306,"stem":2307},"Філософія shadcn\u002Fui: \"Not a Component Library\"","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-philosophy","03.javascript\u002F04.react\u002F07.ui-libraries\u002F02.shadcn-philosophy",{"title":2309,"path":2310,"stem":2311},"Установка та Налаштування shadcn\u002Fui","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-installation","03.javascript\u002F04.react\u002F07.ui-libraries\u002F03.shadcn-installation",{"title":2313,"path":2314,"stem":2315},"Базові Компоненти shadcn\u002Fui: Фундамент Інтерфейсу","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-basics","03.javascript\u002F04.react\u002F07.ui-libraries\u002F04.shadcn-components-basics",{"title":2317,"path":2318,"stem":2319},"Компоненти Форм: Побудова Інтерактивних Form","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-forms","03.javascript\u002F04.react\u002F07.ui-libraries\u002F05.shadcn-components-forms",{"title":2321,"path":2322,"stem":2323},"Складні Компоненти: Dialog, Dropdown, Table та Command","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-advanced","03.javascript\u002F04.react\u002F07.ui-libraries\u002F06.shadcn-components-advanced",{"title":2325,"icon":2326,"path":2327,"stem":2328,"children":2329,"page":59},"TypeScript","i-devicon-typescript","\u002Fjavascript\u002Ftypescript","03.javascript\u002F05.typescript",[2330,2334,2338,2342,2346,2350,2354,2358],{"title":2331,"path":2332,"stem":2333},"TypeScript: Броня для вашого коду","\u002Fjavascript\u002Ftypescript\u002Fintro-and-basic-types","03.javascript\u002F05.typescript\u002F01.intro-and-basic-types",{"title":2335,"path":2336,"stem":2337},"Майстерність Моделювання Даних: Інтерфейси та Просунуті Типи","\u002Fjavascript\u002Ftypescript\u002Finterfaces-and-advanced-types","03.javascript\u002F05.typescript\u002F02.interfaces-and-advanced-types",{"title":2339,"path":2340,"stem":2341},"Алхімія Типів: Generics та Utility Types","\u002Fjavascript\u002Ftypescript\u002Fgenerics-and-utilities","03.javascript\u002F05.typescript\u002F03.generics-and-utilities",{"title":2343,"path":2344,"stem":2345},"Архітектура та Шаблони: Класи в TypeScript","\u002Fjavascript\u002Ftypescript\u002Fclasses-and-oop","03.javascript\u002F05.typescript\u002F04.classes-and-oop",{"title":2347,"path":2348,"stem":2349},"Продакшн та Екосистема: Advanced Config & Workflow","\u002Fjavascript\u002Ftypescript\u002Fadvanced-patterns-and-config","03.javascript\u002F05.typescript\u002F05.advanced-patterns-and-config",{"title":2351,"path":2352,"stem":2353},"TypeScript у світі React","\u002Fjavascript\u002Ftypescript\u002Freact-basics","03.javascript\u002F05.typescript\u002F06.react-basics",{"title":2355,"path":2356,"stem":2357},"React + TypeScript: Продвинуті патерни","\u002Fjavascript\u002Ftypescript\u002Freact-advanced","03.javascript\u002F05.typescript\u002F07.react-advanced",{"title":2359,"path":2360,"stem":2361},"React + TypeScript: Екосистема та бібліотеки","\u002Fjavascript\u002Ftypescript\u002Freact-ecosystem","03.javascript\u002F05.typescript\u002F08.react-ecosystem",{"title":2363,"path":2364,"stem":2365},"Atomic Design","\u002Fjavascript\u002Fatomic-design","03.javascript\u002F2.atomic-design",{"title":2367,"icon":2368,"path":2369,"stem":2370,"children":2371,"page":59},"Java","i-devicon-java","\u002Fjava","04.java",[2372,2375,2378,2382,2386,2390,2394],{"title":162,"path":2373,"stem":2374},"\u002Fjava\u002Fdata-mapper-part1","04.java\u002F01.data-mapper-part1",{"title":166,"path":2376,"stem":2377},"\u002Fjava\u002Fdata-mapper-part2","04.java\u002F02.data-mapper-part2",{"title":2379,"path":2380,"stem":2381},"Service Layer: Організація бізнес-логіки","\u002Fjava\u002Fservice-layer","04.java\u002F03.service-layer",{"title":2383,"path":2384,"stem":2385},"Rich Domain Model та State Pattern","\u002Fjava\u002Frich-domain-model","04.java\u002F04.rich-domain-model",{"title":2387,"path":2388,"stem":2389},"Патерни для складної бізнес-логіки","\u002Fjava\u002Fbusiness-logic-patterns","04.java\u002F05.business-logic-patterns",{"title":2391,"path":2392,"stem":2393},"Обробка помилок та валідація","\u002Fjava\u002Ferror-handling-validation","04.java\u002F06.error-handling-validation",{"title":2395,"path":2396,"stem":2397,"children":2398,"page":59},"Проектування баз даних","\u002Fjava\u002Fpr2","04.java\u002Fpr2",[2399,2403,2407,2411,2415,2419,2423,2427,2431,2435,2439,2443,2447,2451,2455,2459,2463,2467,2471,2475,2479,2483,2487,2491,2495,2499,2503,2507,2511,2515,2519,2523,2527,2531,2535,2539,2543],{"title":2400,"path":2401,"stem":2402},"Концептуальне моделювання: Мистецтво розуміння предметної області","\u002Fjava\u002Fpr2\u002Fconceptual-modeling","04.java\u002Fpr2\u002F01.conceptual-modeling",{"title":2404,"path":2405,"stem":2406},"Логічне моделювання: Від бізнес-ідей до структур даних","\u002Fjava\u002Fpr2\u002Flogical-modeling","04.java\u002Fpr2\u002F02.logical-modeling",{"title":2408,"path":2409,"stem":2410},"Нормалізація: Гігієна даних та боротьба з аномаліями","\u002Fjava\u002Fpr2\u002Fnormalization","04.java\u002Fpr2\u002F03.normalization",{"title":2412,"path":2413,"stem":2414},"Фізична схема: Від абстракції до DDL","\u002Fjava\u002Fpr2\u002Fphysical-schema","04.java\u002Fpr2\u002F04.physical-schema",{"title":2416,"path":2417,"stem":2418},"Архітектурна класифікація таблиць","\u002Fjava\u002Fpr2\u002Ftable-classification","04.java\u002Fpr2\u002F05.table-classification",{"title":2420,"path":2421,"stem":2422},"Database Migrations: Версіонування схеми з Flyway","\u002Fjava\u002Fpr2\u002Fdatabase-migrations","04.java\u002Fpr2\u002F06.database-migrations",{"title":2424,"path":2425,"stem":2426},"А що, якби це була не реляційна БД?","\u002Fjava\u002Fpr2\u002Fbeyond-relational","04.java\u002Fpr2\u002F07.beyond-relational",{"title":2428,"path":2429,"stem":2430},"Object-Relational Impedance Mismatch: Два світи, що не хочуть дружити","\u002Fjava\u002Fpr2\u002Fimpedance-mismatch","04.java\u002Fpr2\u002F09.impedance-mismatch",{"title":2432,"path":2433,"stem":2434},"JDBC: Перший контакт із базою даних","\u002Fjava\u002Fpr2\u002Fjdbc-fundamentals","04.java\u002Fpr2\u002F10.jdbc-fundamentals",{"title":2436,"path":2437,"stem":2438},"Якість коду: Spotless, SpotBugs та SonarQube","\u002Fjava\u002Fpr2\u002F10a.code-quality","04.java\u002Fpr2\u002F10a.code-quality",{"title":2440,"path":2441,"stem":2442},"Connection Pool: Патерн Object Pool для JDBC-з'єднань","\u002Fjava\u002Fpr2\u002Fconnection-pool","04.java\u002Fpr2\u002F11.connection-pool",{"title":2444,"path":2445,"stem":2446},"Row Data Gateway: Об'єкт як обгортка рядка таблиці","\u002Fjava\u002Fpr2\u002Frow-data-gateway","04.java\u002Fpr2\u002F12.row-data-gateway",{"title":2448,"path":2449,"stem":2450},"Table Data Gateway: Фасад таблиці як архітектурний відступ","\u002Fjava\u002Fpr2\u002Ftable-data-gateway","04.java\u002Fpr2\u002F13.table-data-gateway",{"title":2452,"path":2453,"stem":2454},"Repository + Data Mapper: Правильна шарова архітектура з JDBC","\u002Fjava\u002Fpr2\u002Frepository-data-mapper","04.java\u002Fpr2\u002F14.repository-data-mapper",{"title":2456,"path":2457,"stem":2458},"Identity Map: Кешування сутностей у рамках сесії","\u002Fjava\u002Fpr2\u002Fidentity-map","04.java\u002Fpr2\u002F15.identity-map",{"title":2460,"path":2461,"stem":2462},"Unit of Work: Відстеження змін і координація JDBC-транзакцій","\u002Fjava\u002Fpr2\u002Funit-of-work","04.java\u002Fpr2\u002F16.unit-of-work",{"title":2464,"path":2465,"stem":2466},"Strategy: Замінювані SQL-стратегії для підтримки різних СУБД","\u002Fjava\u002Fpr2\u002Fstrategy-sql","04.java\u002Fpr2\u002F17.strategy-sql",{"title":2468,"path":2469,"stem":2470},"Proxy: Lazy Loading для One-To-Many колекцій","\u002Fjava\u002Fpr2\u002Fproxy-lazy-loading","04.java\u002Fpr2\u002F18.proxy-lazy-loading",{"title":2472,"path":2473,"stem":2474},"Generic Repository через Java Reflection: анотації та динамічний SQL","\u002Fjava\u002Fpr2\u002Fgeneric-repository-reflection","04.java\u002Fpr2\u002F19.generic-repository-reflection",{"title":2476,"path":2477,"stem":2478},"Specification Pattern: Композиція бізнес-правил для складних запитів","\u002Fjava\u002Fpr2\u002Fspecification-pattern","04.java\u002Fpr2\u002F20.specification-pattern",{"title":2480,"path":2481,"stem":2482},"Розширені можливості Specification Pattern: підзапити, агрегації та гібридний підхід","\u002Fjava\u002Fpr2\u002F20a.advanced-specifications","04.java\u002Fpr2\u002F20a.advanced-specifications",{"title":2484,"path":2485,"stem":2486},"Асинхронність у JDBC: Від блокуючих викликів до CompletableFuture","\u002Fjava\u002Fpr2\u002Fasynchronous-jdbc","04.java\u002Fpr2\u002F21.asynchronous-jdbc",{"title":2488,"path":2489,"stem":2490},"Інтеграційне тестування JDBC-репозиторіїв: Embedded H2 та патерн AAA","\u002Fjava\u002Fpr2\u002Fintegration-testing-h2","04.java\u002Fpr2\u002F22.integration-testing-h2",{"title":2492,"path":2493,"stem":2494},"Testcontainers: Тестування з реальною PostgreSQL у Docker-контейнерах","\u002Fjava\u002Fpr2\u002Fintegration-testing-testcontainers","04.java\u002Fpr2\u002F23.integration-testing-testcontainers",{"title":2496,"path":2497,"stem":2498},"Google Guice: Впровадження залежностей у JavaFX-проєкті","\u002Fjava\u002Fpr2\u002Fdependency-injection-guice","04.java\u002Fpr2\u002F24.dependency-injection-guice",{"title":2500,"path":2501,"stem":2502},"JavaFX: Основи побудови графічних інтерфейсів","\u002Fjava\u002Fpr2\u002Fjavafx-fundamentals","04.java\u002Fpr2\u002F25.javafx-fundamentals",{"title":2504,"path":2505,"stem":2506},"Properties та Bindings: Реактивність у JavaFX","\u002Fjava\u002Fpr2\u002Fjavafx-properties-bindings","04.java\u002Fpr2\u002F26.javafx-properties-bindings",{"title":2508,"path":2509,"stem":2510},"MVC vs MVP vs MVVM: Еволюція архітектурних патернів UI","\u002Fjava\u002Fpr2\u002Fui-architecture-patterns","04.java\u002Fpr2\u002F27.ui-architecture-patterns",{"title":2512,"path":2513,"stem":2514},"MVVM на практиці: Побудова ViewModel","\u002Fjava\u002Fpr2\u002Fmvvm-viewmodel-implementation","04.java\u002Fpr2\u002F28.mvvm-viewmodel-implementation",{"title":2516,"path":2517,"stem":2518},"View та Controller: Зв'язування з ViewModel через FXML","\u002Fjava\u002Fpr2\u002Fmvvm-view-controller","04.java\u002Fpr2\u002F29.mvvm-view-controller",{"title":2520,"path":2521,"stem":2522},"Інтеграція MVVM з Guice: Автоматична ін'єкція залежностей","\u002Fjava\u002Fpr2\u002Fmvvm-guice-integration","04.java\u002Fpr2\u002F30.mvvm-guice-integration",{"title":2524,"path":2525,"stem":2526},"Валідація та обробка помилок у MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-validation-error-handling","04.java\u002Fpr2\u002F31.mvvm-validation-error-handling",{"title":2528,"path":2529,"stem":2530},"Навігація та управління екранами у JavaFX MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-navigation-screen-management","04.java\u002Fpr2\u002F32.mvvm-navigation-screen-management",{"title":2532,"path":2533,"stem":2534},"Тестування JavaFX MVVM-додатків","\u002Fjava\u002Fpr2\u002Fmvvm-testing","04.java\u002Fpr2\u002F33.mvvm-testing",{"title":2536,"path":2537,"stem":2538},"Стилізація та теми у JavaFX: CSS та User Experience","\u002Fjava\u002Fpr2\u002Fjavafx-styling-themes","04.java\u002Fpr2\u002F34.javafx-styling-themes",{"title":2540,"path":2541,"stem":2542},"AtlantaFX: Сучасні теми для JavaFX додатків","\u002Fjava\u002Fpr2\u002Fatlantafx-modern-themes","04.java\u002Fpr2\u002F35.atlantafx-modern-themes",{"title":2544,"path":2545,"stem":2546},"Пакування та розповсюдження JavaFX-додатків","\u002Fjava\u002Fpr2\u002Fjar-packaging-distribution","04.java\u002Fpr2\u002F36.jar-packaging-distribution",{"title":2548,"icon":2549,"path":2550,"stem":2551,"children":2552,"page":59},"Python","i-devicon-python","\u002Fpython","05.python",[2553,2557,2560,2564,2568,2572,2576,2580,2584,2588,2592,2596,2600,2604,2608,2645],{"title":2554,"path":2555,"stem":2556},"Модулі, Пакети та Віртуальні Середовища","\u002Fpython\u002Fmodules-packages-venv","05.python\u002F00.modules-packages-venv",{"title":71,"path":2558,"stem":2559},"\u002Fpython\u002Fclasses-objects","05.python\u002F01.classes-objects",{"title":2561,"path":2562,"stem":2563},"Інкапсуляція, Керування Доступом та Властивості","\u002Fpython\u002Fencapsulation","05.python\u002F02.encapsulation",{"title":2565,"path":2566,"stem":2567},"Наслідування, MRO та суперсила super()","\u002Fpython\u002Finheritance-mro","05.python\u002F03.inheritance-mro",{"title":2569,"path":2570,"stem":2571},"Абстракція — ABC проти Статичних Протоколів (PEP 544)","\u002Fpython\u002Fabstraction-protocols","05.python\u002F04.abstraction-protocols",{"title":2573,"path":2574,"stem":2575},"Магічні методи (Dunder) та Емуляція протоколів","\u002Fpython\u002Fdunder-methods","05.python\u002F05.dunder-methods",{"title":2577,"path":2578,"stem":2579},"Декоратори та Керування життєвим циклом методів","\u002Fpython\u002Fdecorators-static-class","05.python\u002F06.decorators-static-class",{"title":2581,"path":2582,"stem":2583},"Дескриптори — Магія доступу до атрибутів","\u002Fpython\u002Fdescriptors","05.python\u002F07.descriptors",{"title":2585,"path":2586,"stem":2587},"Метакласи — Динамічне створення класів під капотом CPython","\u002Fpython\u002Fmetaclasses","05.python\u002F08.metaclasses",{"title":2589,"path":2590,"stem":2591},"Dataclasses, NamedTuple та сучасні контейнери Python","\u002Fpython\u002Fmodern-containers","05.python\u002F09.modern-containers",{"title":2593,"path":2594,"stem":2595},"GIL та модель конкурентності CPython — фундамент перед потоками і процесами","\u002Fpython\u002Fgil-concurrency-intro","05.python\u002F11.gil-concurrency-intro",{"title":2597,"path":2598,"stem":2599},"Threading — конкурентність для I\u002FO-bound задач","\u002Fpython\u002Fthreading","05.python\u002F12.threading",{"title":2601,"path":2602,"stem":2603},"Multiprocessing — справжній паралелізм для CPU-bound задач","\u002Fpython\u002Fmultiprocessing","05.python\u002F13.multiprocessing",{"title":2605,"path":2606,"stem":2607},"asyncio — кооперативна конкурентність та event loop","\u002Fpython\u002Fasyncio","05.python\u002F14.asyncio",{"title":2609,"icon":92,"path":2610,"stem":2611,"children":2612,"page":59},"FastAPI","\u002Fpython\u002Ffastapi","05.python\u002Ffastapi",[2613,2617,2621,2625,2629,2633,2637,2641],{"title":2614,"path":2615,"stem":2616},"Глибокий Typing та Pydantic v2 — від анотацій до валідації","\u002Fpython\u002Ffastapi\u002Ftyping-pydantic","05.python\u002Ffastapi\u002F15.typing-pydantic",{"title":2618,"path":2619,"stem":2620},"WSGI, ASGI та Python Web-екосистема","\u002Fpython\u002Ffastapi\u002Fwsgi-asgi-ecosystem","05.python\u002Ffastapi\u002F16.wsgi-asgi-ecosystem",{"title":2622,"path":2623,"stem":2624},"FastAPI: Перший додаток, Uvicorn та OpenAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-intro","05.python\u002Ffastapi\u002F17.fastapi-intro",{"title":2626,"path":2627,"stem":2628},"Маршрутизація, параметри запитів та APIRouter","\u002Fpython\u002Ffastapi\u002Ffastapi-routing-params","05.python\u002Ffastapi\u002F18.fastapi-routing-params",{"title":2630,"path":2631,"stem":2632},"Pydantic v2 у FastAPI — схеми, валідація та серіалізація","\u002Fpython\u002Ffastapi\u002Ffastapi-pydantic-schemas","05.python\u002Ffastapi\u002F19.fastapi-pydantic-schemas",{"title":2634,"path":2635,"stem":2636},"Dependency Injection — серце архітектури FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-dependency-injection","05.python\u002Ffastapi\u002F20.fastapi-dependency-injection",{"title":2638,"path":2639,"stem":2640},"Обробка помилок, Middleware та CORS у FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-errors-middleware","05.python\u002Ffastapi\u002F21.fastapi-errors-middleware",{"title":2642,"path":2643,"stem":2644},"SQLAlchemy 2.0 — ORM, Core та Async Engine","\u002Fpython\u002Ffastapi\u002Fsqlalchemy-orm","05.python\u002Ffastapi\u002F22.sqlalchemy-orm",{"title":2646,"path":2647,"stem":2648},"[object Object]","\u002Fpython\u002Foop-plan","05.python\u002Foop-plan",{"title":2650,"icon":2651,"path":2652,"stem":2653,"children":2654,"page":59},"Бази даних","i-lucide-database","\u002Fdatabases","06.databases",[2655,2685,2708,2745,2774,2792,2826,2838,2847],{"title":2656,"icon":2657,"path":2658,"stem":2659,"children":2660,"page":59},"Intro","i-lucide-play","\u002Fdatabases\u002Fintro","06.databases\u002F01.intro",[2661,2665,2669,2673,2677,2681],{"title":2662,"path":2663,"stem":2664},"Введення в теорію баз даних","\u002Fdatabases\u002Fintro\u002Fintroduction-to-databases","06.databases\u002F01.intro\u002F01.introduction-to-databases",{"title":2666,"path":2667,"stem":2668},"Реляційна модель даних","\u002Fdatabases\u002Fintro\u002Frelational-model-theory","06.databases\u002F01.intro\u002F02.relational-model-theory",{"title":2670,"path":2671,"stem":2672},"ER-моделювання","\u002Fdatabases\u002Fintro\u002Fer-modeling","06.databases\u002F01.intro\u002F03.er-modeling",{"title":2674,"path":2675,"stem":2676},"Логічне проектування БД","\u002Fdatabases\u002Fintro\u002Flogical-schema","06.databases\u002F01.intro\u002F04.logical-schema",{"title":2678,"path":2679,"stem":2680},"Класифікація таблиць","\u002Fdatabases\u002Fintro\u002Ftable-classification","06.databases\u002F01.intro\u002F05.table-classification",{"title":2682,"path":2683,"stem":2684},"PlantUML для баз даних","\u002Fdatabases\u002Fintro\u002Fplantuml-diagrams","06.databases\u002F01.intro\u002F06.plantuml-diagrams",{"title":2686,"icon":2651,"path":2687,"stem":2688,"children":2689,"page":59},"MS SQL Server Start","\u002Fdatabases\u002Fms-sql-server-start","06.databases\u002F02.ms-sql-server-start",[2690,2694,2700,2704],{"title":2691,"path":2692,"stem":2693},"Типи даних у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fdata-types","06.databases\u002F02.ms-sql-server-start\u002F01.data-types",{"title":2695,"path":2696,"stem":2697,"children":2698},"Індекси у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-indexes","06.databases\u002F02.ms-sql-server-start\u002F02.sql-indexes",[2699],{"title":2695,"path":2696,"stem":2697},{"title":2701,"path":2702,"stem":2703},"Системні бази даних MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsystem-databases","06.databases\u002F02.ms-sql-server-start\u002F03.system-databases",{"title":2705,"path":2706,"stem":2707},"Огляд мови SQL та запитів","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-queries-overview","06.databases\u002F02.ms-sql-server-start\u002F04.sql-queries-overview",{"title":2709,"icon":2651,"path":2710,"stem":2711,"children":2712,"page":59},"SQL","\u002Fdatabases\u002Fsql","06.databases\u002F03.sql",[2713,2717,2721,2725,2729,2733,2737,2741],{"title":2714,"path":2715,"stem":2716},"Налаштування демонстраційної бази даних","\u002Fdatabases\u002Fsql\u002Fsample-database-setup","06.databases\u002F03.sql\u002F00.sample-database-setup",{"title":2718,"path":2719,"stem":2720},"DDL - Створення таблиць (CREATE TABLE)","\u002Fdatabases\u002Fsql\u002Fddl-create-table","06.databases\u002F03.sql\u002F01.ddl-create-table",{"title":2722,"path":2723,"stem":2724},"DDL - Зміна та видалення таблиць (ALTER, DROP)","\u002Fdatabases\u002Fsql\u002Fddl-alter-drop-table","06.databases\u002F03.sql\u002F02.ddl-alter-drop-table",{"title":2726,"path":2727,"stem":2728},"SELECT запити - Основи","\u002Fdatabases\u002Fsql\u002Fselect-queries-fundamentals","06.databases\u002F03.sql\u002F03.select-queries-fundamentals",{"title":2730,"path":2731,"stem":2732},"SELECT запити - Розширені можливості","\u002Fdatabases\u002Fsql\u002Fselect-queries-advanced","06.databases\u002F03.sql\u002F04.select-queries-advanced",{"title":2734,"path":2735,"stem":2736},"INSERT запити - Додавання даних","\u002Fdatabases\u002Fsql\u002Finsert-queries","06.databases\u002F03.sql\u002F05.insert-queries",{"title":2738,"path":2739,"stem":2740},"UPDATE та DELETE запити","\u002Fdatabases\u002Fsql\u002Fupdate-delete-queries","06.databases\u002F03.sql\u002F06.update-delete-queries",{"title":2742,"path":2743,"stem":2744},"Транзакції в SQL","\u002Fdatabases\u002Fsql\u002Ftransactions","06.databases\u002F03.sql\u002F07.transactions",{"title":2746,"icon":2651,"path":2747,"stem":2748,"children":2749,"page":59},"Multi Table Databases","\u002Fdatabases\u002Fmulti-table-databases","06.databases\u002F04.multi-table-databases",[2750,2754,2758,2762,2766,2770],{"title":2751,"path":2752,"stem":2753},"Зв'язки та нормалізація БД","\u002Fdatabases\u002Fmulti-table-databases\u002Frelationships-and-normalization","06.databases\u002F04.multi-table-databases\u002F00.relationships-and-normalization",{"title":2755,"path":2756,"stem":2757},"INNER JOIN - З'єднання таблиць","\u002Fdatabases\u002Fmulti-table-databases\u002Finner-join","06.databases\u002F04.multi-table-databases\u002F01.inner-join",{"title":2759,"path":2760,"stem":2761},"OUTER JOINs - LEFT, RIGHT, FULL","\u002Fdatabases\u002Fmulti-table-databases\u002Fouter-joins","06.databases\u002F04.multi-table-databases\u002F02.outer-joins",{"title":2763,"path":2764,"stem":2765},"CROSS та SELF JOINs","\u002Fdatabases\u002Fmulti-table-databases\u002Fcross-self-joins","06.databases\u002F04.multi-table-databases\u002F03.cross-self-joins",{"title":2767,"path":2768,"stem":2769},"Підзапити (Subqueries)","\u002Fdatabases\u002Fmulti-table-databases\u002Fsubqueries","06.databases\u002F04.multi-table-databases\u002F04.subqueries",{"title":2771,"path":2772,"stem":2773},"Агрегації з JOIN","\u002Fdatabases\u002Fmulti-table-databases\u002Faggregations-with-joins","06.databases\u002F04.multi-table-databases\u002F05.aggregations-with-joins",{"title":2775,"icon":2776,"path":2777,"stem":2778,"children":2779,"page":59},"Aggregate Functions","i-lucide-calculator","\u002Fdatabases\u002Faggregate-functions","06.databases\u002F05.aggregate-functions",[2780,2784,2788],{"title":2781,"path":2782,"stem":2783},"Функції агрегування в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fintroduction-aggregate-functions","06.databases\u002F05.aggregate-functions\u002F01.introduction-aggregate-functions",{"title":2785,"path":2786,"stem":2787},"Групування даних в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fgrouping-data","06.databases\u002F05.aggregate-functions\u002F02.grouping-data",{"title":2789,"path":2790,"stem":2791},"Підзапити з агрегатними функціями","\u002Fdatabases\u002Faggregate-functions\u002Fsubqueries-aggregates","06.databases\u002F05.aggregate-functions\u002F03.subqueries-aggregates",{"title":2793,"icon":2794,"path":2795,"stem":2796,"children":2797,"page":59},"Тригери та зберігаємі процедури","i-lucide-database-zap","\u002Fdatabases\u002Ftriggers-stored-procedures","06.databases\u002F07.triggers-stored-procedures",[2798,2802,2806,2810,2814,2818,2822],{"title":2799,"path":2800,"stem":2801},"DML-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fdml-triggers","06.databases\u002F07.triggers-stored-procedures\u002F01.dml-triggers",{"title":2803,"path":2804,"stem":2805},"DDL-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fddl-triggers","06.databases\u002F07.triggers-stored-procedures\u002F02.ddl-triggers",{"title":2807,"path":2808,"stem":2809},"Transact-SQL розширення","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransact-sql-extensions","06.databases\u002F07.triggers-stored-procedures\u002F03.transact-sql-extensions",{"title":2811,"path":2812,"stem":2813},"Транзакції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransactions","06.databases\u002F07.triggers-stored-procedures\u002F04.transactions",{"title":2815,"path":2816,"stem":2817},"Зберігаємі процедури","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fstored-procedures","06.databases\u002F07.triggers-stored-procedures\u002F05.stored-procedures",{"title":2819,"path":2820,"stem":2821},"Користувацькі функції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fuser-defined-functions","06.databases\u002F07.triggers-stored-procedures\u002F06.user-defined-functions",{"title":2823,"path":2824,"stem":2825},"Безпека баз даних","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fsecurity","06.databases\u002F07.triggers-stored-procedures\u002F08.security",{"title":2823,"icon":793,"path":2827,"stem":2828,"children":2829,"page":59},"\u002Fdatabases\u002Fsecurity","06.databases\u002F08.security",[2830,2834],{"title":2831,"path":2832,"stem":2833},"Вступ до безпеки баз даних","\u002Fdatabases\u002Fsecurity\u002Fintroduction","06.databases\u002F08.security\u002F01.introduction",{"title":2835,"path":2836,"stem":2837},"Системні представлення та метадані","\u002Fdatabases\u002Fsecurity\u002Fsystem-views","06.databases\u002F08.security\u002F02.system-views",{"title":2839,"icon":2840,"path":2841,"stem":2842,"children":2843,"page":59},"Резервне копіювання та відновлення","i-lucide-database-backup","\u002Fdatabases\u002Fbackup-recovery","06.databases\u002F09.backup-recovery",[2844],{"title":2839,"path":2845,"stem":2846},"\u002Fdatabases\u002Fbackup-recovery\u002Fbackup-restore","06.databases\u002F09.backup-recovery\u002F01.backup-restore",{"title":2848,"icon":2849,"path":2850,"stem":2851,"children":2852,"page":59},"Повнотекстовий пошук","i-lucide-search","\u002Fdatabases\u002Ffull-text-search","06.databases\u002F10.full-text-search",[2853],{"title":2848,"path":2854,"stem":2855},"\u002Fdatabases\u002Ffull-text-search\u002Ffull-text-search","06.databases\u002F10.full-text-search\u002F01.full-text-search",{"title":2857,"icon":2858,"path":2859,"stem":2860,"children":2861,"page":59},"Tools","i-lucide-wrench","\u002Ftools","07.tools",[2862,2938],{"title":2863,"icon":2864,"path":2865,"stem":2866,"children":2867},"Docker","i-simple-icons-docker","\u002Ftools\u002Fdocker","07.tools\u002F01.docker\u002Findex",[2868,2870,2874,2878,2882,2886,2890,2894,2898,2902,2906,2910,2914,2918,2922,2926,2930,2934],{"title":2869,"path":2865,"stem":2866},"Docker: від нуля до production",{"title":2871,"path":2872,"stem":2873},"Контейнеризація — від проблеми до рішення","\u002Ftools\u002Fdocker\u002Fcontainerization-concept","07.tools\u002F01.docker\u002F01.containerization-concept",{"title":2875,"path":2876,"stem":2877},"Docker — що це і навіщо?","\u002Ftools\u002Fdocker\u002Fdocker-what-and-why","07.tools\u002F01.docker\u002F02.docker-what-and-why",{"title":2879,"path":2880,"stem":2881},"Архітектура Docker Engine","\u002Ftools\u002Fdocker\u002Fdocker-architecture","07.tools\u002F01.docker\u002F03.docker-architecture",{"title":2883,"path":2884,"stem":2885},"Встановлення Docker","\u002Ftools\u002Fdocker\u002Finstallation","07.tools\u002F01.docker\u002F04.installation",{"title":2887,"path":2888,"stem":2889},"Перший контейнер — docker run","\u002Ftools\u002Fdocker\u002Ffirst-container","07.tools\u002F01.docker\u002F05.first-container",{"title":2891,"path":2892,"stem":2893},"Життєвий цикл контейнера","\u002Ftools\u002Fdocker\u002Fcontainer-lifecycle","07.tools\u002F01.docker\u002F06.container-lifecycle",{"title":2895,"path":2896,"stem":2897},"Docker Images — фундаментальні концепції","\u002Ftools\u002Fdocker\u002Fdocker-images-fundamentals","07.tools\u002F01.docker\u002F07.docker-images-fundamentals",{"title":2899,"path":2900,"stem":2901},"Dockerfile — основи","\u002Ftools\u002Fdocker\u002Fdockerfile-basics","07.tools\u002F01.docker\u002F08.dockerfile-basics",{"title":2903,"path":2904,"stem":2905},"Dockerfile — просунуті техніки","\u002Ftools\u002Fdocker\u002Fdockerfile-advanced","07.tools\u002F01.docker\u002F09.dockerfile-advanced",{"title":2907,"path":2908,"stem":2909},"Build Context та кешування шарів","\u002Ftools\u002Fdocker\u002Fbuild-context-and-cache","07.tools\u002F01.docker\u002F10.build-context-and-cache",{"title":2911,"path":2912,"stem":2913},"Реєстри Docker-образів","\u002Ftools\u002Fdocker\u002Fimage-registries","07.tools\u002F01.docker\u002F11.image-registries",{"title":2915,"path":2916,"stem":2917},"Контейнеризація .NET додатків","\u002Ftools\u002Fdocker\u002Fdotnet-containerization","07.tools\u002F01.docker\u002F12.dotnet-containerization",{"title":2919,"path":2920,"stem":2921},"Томи та збереження даних","\u002Ftools\u002Fdocker\u002Fvolumes-and-data","07.tools\u002F01.docker\u002F13.volumes-and-data",{"title":2923,"path":2924,"stem":2925},"Основи мережі в Docker","\u002Ftools\u002Fdocker\u002Fnetworking-basics","07.tools\u002F01.docker\u002F14.networking-basics",{"title":2927,"path":2928,"stem":2929},"Змінні оточення та конфігурація","\u002Ftools\u002Fdocker\u002Fenvironment-and-configuration","07.tools\u002F01.docker\u002F15.environment-and-configuration",{"title":2931,"path":2932,"stem":2933},"Docker Compose — оркестрація контейнерів","\u002Ftools\u002Fdocker\u002Fdocker-compose-basics","07.tools\u002F01.docker\u002F16.docker-compose-basics",{"title":2935,"path":2936,"stem":2937},"Docker Compose — Multi-Service застосунки","\u002Ftools\u002Fdocker\u002Fcompose-multi-service","07.tools\u002F01.docker\u002F17.compose-multi-service",{"title":2939,"icon":2940,"path":2941,"stem":2942,"children":2943},"Kubernetes","simple-icons:kubernetes","\u002Ftools\u002Fkubernetes","07.tools\u002F02.kubernetes\u002Findex",[2944,2946,2950,2954,2958,2962,2966,2970,2974],{"title":2945,"path":2941,"stem":2942},"Kubernetes: від розробки до production",{"title":2947,"path":2948,"stem":2949},"Kubernetes — коли Docker Compose більше не вистачає","\u002Ftools\u002Fkubernetes\u002Fwhy-kubernetes","07.tools\u002F02.kubernetes\u002F01.why-kubernetes",{"title":2951,"path":2952,"stem":2953},"Архітектура Kubernetes — анатомія кластера","\u002Ftools\u002Fkubernetes\u002Fkubernetes-architecture","07.tools\u002F02.kubernetes\u002F02.kubernetes-architecture",{"title":2955,"path":2956,"stem":2957},"Локальне середовище — minikube, kind та k3s","\u002Ftools\u002Fkubernetes\u002Flocal-environment","07.tools\u002F02.kubernetes\u002F03.local-environment",{"title":2959,"path":2960,"stem":2961},"Pod — атомарна одиниця Kubernetes","\u002Ftools\u002Fkubernetes\u002Fpods-and-containers","07.tools\u002F02.kubernetes\u002F04.pods-and-containers",{"title":2963,"path":2964,"stem":2965},"Патерни використання Pod","\u002Ftools\u002Fkubernetes\u002Fpod-patterns","07.tools\u002F02.kubernetes\u002F05.pod-patterns",{"title":2967,"path":2968,"stem":2969},"Deployment — декларативне управління Pod","\u002Ftools\u002Fkubernetes\u002Fdeployment-basics","07.tools\u002F02.kubernetes\u002F06.deployment-basics",{"title":2971,"path":2972,"stem":2973},"Rolling Updates та управління життєвим циклом Deployment","\u002Ftools\u002Fkubernetes\u002Fdeployment-rolling-updates","07.tools\u002F02.kubernetes\u002F07.deployment-rolling-updates",{"title":2975,"path":2976,"stem":2977},"Service — мережева абстракція для Pod","\u002Ftools\u002Fkubernetes\u002Fservices-networking","07.tools\u002F02.kubernetes\u002F08.services-networking",{"title":2979,"icon":2980,"path":2981,"stem":2982,"children":2983,"page":59},"Software Engineering","i-lucide-code-2","\u002Fsoftware-engineering","09.software-engineering",[2984,2988,2992,2996,3000,3004,3008,3012,3016,3020,3024],{"title":2985,"path":2986,"stem":2987},"1. Аналіз предметної області. Експертні знання та складність","\u002Fsoftware-engineering\u002Fintro-subdomains","09.software-engineering\u002F01.intro-subdomains",{"title":2989,"path":2990,"stem":2991},"2. Обмежені контексти. Інтеграція обмежених контекстів","\u002Fsoftware-engineering\u002Fintegrating-limited-contexts","09.software-engineering\u002F02.integrating-limited-contexts",{"title":2993,"path":2994,"stem":2995},"3. Реалізація простої бізнес-логіки","\u002Fsoftware-engineering\u002Fsimple","09.software-engineering\u002F03.simple",{"title":2997,"path":2998,"stem":2999},"4. Опрацювання складної бізнес-логіки","\u002Fsoftware-engineering\u002Fcomplex-business-logic","09.software-engineering\u002F04.complex-business-logic",{"title":3001,"path":3002,"stem":3003},"5. Моделювання фактора часу. Подієво-орієнтована архітектура.","\u002Fsoftware-engineering\u002Fmodelling-the-time-factor","09.software-engineering\u002F05.modelling-the-time-factor",{"title":3005,"path":3006,"stem":3007},"6. Архітектурні патерни","\u002Fsoftware-engineering\u002Farchitectural-patterns","09.software-engineering\u002F06.architectural-patterns",{"title":3009,"path":3010,"stem":3011},"Паттерни взаємодії","\u002Fsoftware-engineering\u002Fpatterns-of-interaction","09.software-engineering\u002F07.patterns-of-interaction",{"title":3013,"path":3014,"stem":3015},"Евристика проєктування","\u002Fsoftware-engineering\u002Fdesign-heuristics","09.software-engineering\u002F08.design-heuristics",{"title":3017,"path":3018,"stem":3019},"Еволюція проєктних рішень","\u002Fsoftware-engineering\u002Fevolution-of-design-solutions","09.software-engineering\u002F09.evolution-of-design-solutions",{"title":3021,"path":3022,"stem":3023},"EventStorming","\u002Fsoftware-engineering\u002Feventstorming","09.software-engineering\u002F10.eventstorming",{"title":3025,"path":3026,"stem":3027},"DDD на практиці","\u002Fsoftware-engineering\u002Fddd-in-practice","09.software-engineering\u002F11.ddd-in-practice",{"title":3029,"icon":943,"path":3030,"stem":3031,"children":3032,"page":59},"DDD","\u002Fddd","10.ddd",[3033,3037,3041,3045,3049,3053,3057,3061,3065,3069,3073,3077,3081],{"title":3034,"path":3035,"stem":3036},"Аналіз предметної області","\u002Fddd\u002Fdomain-analysis","10.ddd\u002F01.domain-analysis",{"title":3038,"path":3039,"stem":3040},"Експертні знання про предметну область","\u002Fddd\u002Fdomain-expert-knowledge","10.ddd\u002F02.domain-expert-knowledge",{"title":3042,"path":3043,"stem":3044},"Як осмислити складність предметної області","\u002Fddd\u002Fmanaging-domain-complexity","10.ddd\u002F03.managing-domain-complexity",{"title":3046,"path":3047,"stem":3048},"Інтеграція обмежених контекстів","\u002Fddd\u002Fbounded-context-integration","10.ddd\u002F04.bounded-context-integration",{"title":3050,"path":3051,"stem":3052},"Реалізація простої бізнес-логіки","\u002Fddd\u002Fsimple-business-logic","10.ddd\u002F05.simple-business-logic",{"title":3054,"path":3055,"stem":3056},"Обробка складної бізнес-логіки","\u002Fddd\u002Fcomplex-business-logic","10.ddd\u002F06.complex-business-logic",{"title":3058,"path":3059,"stem":3060},"Моделювання фактора часу","\u002Fddd\u002Ftime-modeling","10.ddd\u002F07.time-modeling",{"title":3062,"path":3063,"stem":3064},"Глава 8. Архітектурні Патерни","\u002Fddd\u002Farchitectural-patterns","10.ddd\u002F08.architectural-patterns",{"title":3066,"path":3067,"stem":3068},"Глава 9. Патерни Взаємодії","\u002Fddd\u002Finteraction-patterns","10.ddd\u002F09.interaction-patterns",{"title":3070,"path":3071,"stem":3072},"Глава 10. Проектні Евристики","\u002Fddd\u002Fdesign-heuristics","10.ddd\u002F10.design-heuristics",{"title":3074,"path":3075,"stem":3076},"Глава 11. Еволюція Проектних Рішень","\u002Fddd\u002Fevolution-of-design-decisions","10.ddd\u002F11.evolution-of-design-decisions",{"title":3078,"path":3079,"stem":3080},"Глава 12. EventStorming","\u002Fddd\u002Fevent-storming","10.ddd\u002F12.event-storming",{"title":3082,"path":3083,"stem":3084},"Глава 13. DDD на Практиці","\u002Fddd\u002Fddd-in-practice","10.ddd\u002F13.ddd-in-practice",{"title":3086,"icon":3087,"path":3088,"stem":3089,"children":3090,"page":59},"Media Streaming","i-lucide-video","\u002Fmedia-streaming","11.media-streaming",[3091,3095,3099,3103,3107,3111,3115],{"title":3092,"path":3093,"stem":3094},"01. Магія Стрімінгу: Що відбувається, коли ви натискаєте \"Play\"","\u002Fmedia-streaming\u002Fintroduction","11.media-streaming\u002F01.introduction",{"title":3096,"path":3097,"stem":3098},"02. Анатомія Медіа: Кодеки, Контейнери та Стиснення","\u002Fmedia-streaming\u002Faudio-video-anatomy","11.media-streaming\u002F02.audio-video-anatomy",{"title":3100,"path":3101,"stem":3102},"03. The Gym: FFmpeg Deep Dive","\u002Fmedia-streaming\u002Fffmpeg-gym","11.media-streaming\u002F03.ffmpeg-gym",{"title":3104,"path":3105,"stem":3106},"04. HLS Protocol: HTTP Live Streaming у Деталях","\u002Fmedia-streaming\u002Fhls-protocol","11.media-streaming\u002F04.hls-protocol",{"title":3108,"path":3109,"stem":3110},"05. DASH Protocol: Відкритий Стандарт","\u002Fmedia-streaming\u002Fdash-protocol","11.media-streaming\u002F05.dash-protocol",{"title":3112,"path":3113,"stem":3114},"06. Масштабування: CDN та Adaptive Bitrate","\u002Fmedia-streaming\u002Fcdn-and-adaptive-bitrate","11.media-streaming\u002F06.cdn-and-adaptive-bitrate",{"title":3116,"path":3117,"stem":3118},"07. Війна із Затримкою (Latency)","\u002Fmedia-streaming\u002Frealtime-latency","11.media-streaming\u002F07.realtime-latency",{"title":3120,"icon":3121,"path":3122,"stem":3123,"children":3124,"page":59},"HTML & CSS","i-devicon-html5","\u002Fhtml-css","12.html-css",[3125,3129,3133,3137,3141,3145,3149,3153,3157,3161,3165,3169,3173,3177,3181,3185,3189,3193,3197,3201,3205,3209,3213,3217,3221,3225,3229,3233,3237,3241],{"title":3126,"path":3127,"stem":3128},"Вступ до HTML. Структура документа","\u002Fhtml-css\u002Fintro-html-structure","12.html-css\u002F01.intro-html-structure",{"title":3130,"path":3131,"stem":3132},"Форматування тексту в HTML","\u002Fhtml-css\u002Fhtml-text-formatting","12.html-css\u002F02.html-text-formatting",{"title":3134,"path":3135,"stem":3136},"Посилання та зображення в HTML","\u002Fhtml-css\u002Fhtml-links-images","12.html-css\u002F03.html-links-images",{"title":3138,"path":3139,"stem":3140},"Списки та таблиці в HTML","\u002Fhtml-css\u002Fhtml-lists-tables","12.html-css\u002F04.html-lists-tables",{"title":3142,"path":3143,"stem":3144},"Форми в HTML","\u002Fhtml-css\u002Fhtml-forms","12.html-css\u002F05.html-forms",{"title":3146,"path":3147,"stem":3148},"Семантичні елементи HTML5","\u002Fhtml-css\u002Fhtml-semantic-elements","12.html-css\u002F06.html-semantic-elements",{"title":3150,"path":3151,"stem":3152},"Мультимедіа та розширені елементи HTML","\u002Fhtml-css\u002Fhtml-multimedia-advanced","12.html-css\u002F07.html-multimedia-advanced",{"title":3154,"path":3155,"stem":3156},"Мікророзмітка та SEO в HTML","\u002Fhtml-css\u002Fhtml-microdata-seo","12.html-css\u002F08.html-microdata-seo",{"title":3158,"path":3159,"stem":3160},"Вступ до CSS. Селектори та специфічність","\u002Fhtml-css\u002Fcss-intro-selectors","12.html-css\u002F09.css-intro-selectors",{"title":3162,"path":3163,"stem":3164},"Блокова модель CSS. Відступи. Box Sizing","\u002Fhtml-css\u002Fcss-box-model","12.html-css\u002F10.css-box-model",{"title":3166,"path":3167,"stem":3168},"Розміри у CSS: повний довідник одиниць і ключових слів","\u002Fhtml-css\u002F10a.css-sizing","12.html-css\u002F10a.css-sizing",{"title":3170,"path":3171,"stem":3172},"Типографіка в CSS. Шрифти та текст","\u002Fhtml-css\u002Fcss-typography","12.html-css\u002F11.css-typography",{"title":3174,"path":3175,"stem":3176},"Кольори та фони в CSS","\u002Fhtml-css\u002Fcss-colors-backgrounds","12.html-css\u002F12.css-colors-backgrounds",{"title":3178,"path":3179,"stem":3180},"Тіні та фільтри в CSS","\u002Fhtml-css\u002F12b.css-shadows-filters","12.html-css\u002F12b.css-shadows-filters",{"title":3182,"path":3183,"stem":3184},"CSS Flexbox: Фундамент гнучких макетів","\u002Fhtml-css\u002Fcss-flexbox-fundamentals","12.html-css\u002F13.css-flexbox-fundamentals",{"title":3186,"path":3187,"stem":3188},"CSS Flexbox: Вирівнювання та Позиціонування","\u002Fhtml-css\u002Fcss-flexbox-alignment-sizing-and-patterns","12.html-css\u002F14.css-flexbox-alignment-sizing-and-patterns",{"title":3190,"path":3191,"stem":3192},"CSS Grid. Двовимірний макет. Частина 1","\u002Fhtml-css\u002Fcss-layout-grid","12.html-css\u002F15.css-layout-grid",{"title":3194,"path":3195,"stem":3196},"CSS Grid. Двовимірний макет. Частина 2","\u002Fhtml-css\u002Fcss-layout-grid-advanced","12.html-css\u002F16.css-layout-grid-advanced",{"title":3198,"path":3199,"stem":3200},"Позиціонування в CSS. Z-index. Stacking Context","\u002Fhtml-css\u002Fcss-positioning","12.html-css\u002F17.css-positioning",{"title":3202,"path":3203,"stem":3204},"CSS Анімації та Переходи","\u002Fhtml-css\u002Fcss-animations-transitions","12.html-css\u002F18.css-animations-transitions",{"title":3206,"path":3207,"stem":3208},"Адаптивний дизайн. Media Queries. Частина 1","\u002Fhtml-css\u002Fcss-responsive-media-queries","12.html-css\u002F19.css-responsive-media-queries",{"title":3210,"path":3211,"stem":3212},"Адаптивний дизайн. Частина 2: clamp(), Container Queries, @layer","\u002Fhtml-css\u002Fcss-responsive-advanced","12.html-css\u002F20.css-responsive-advanced",{"title":3214,"path":3215,"stem":3216},"CSS Custom Properties. Методології. Сучасний CSS","\u002Fhtml-css\u002Fcss-variables-methodologies","12.html-css\u002F21.css-variables-methodologies",{"title":3218,"path":3219,"stem":3220},"Сучасний CSS 2023–2025: Нові можливості","\u002Fhtml-css\u002Fcss-modern-features","12.html-css\u002F22.css-modern-features",{"title":3222,"path":3223,"stem":3224},"CSS Nesting, @layer, @scope та @property: нативний препроцесор","\u002Fhtml-css\u002F22a.css-nesting-modern-syntax","12.html-css\u002F22a.css-nesting-modern-syntax",{"title":3226,"path":3227,"stem":3228},"CSS для форм та інтерактивних станів","\u002Fhtml-css\u002Fcss-forms-interactive-states","12.html-css\u002F23.css-forms-interactive-states",{"title":3230,"path":3231,"stem":3232},"Доступність у CSS (CSS Accessibility)","\u002Fhtml-css\u002Fcss-accessibility","12.html-css\u002F24.css-accessibility",{"title":3234,"path":3235,"stem":3236},"CSS-функції та сучасні sizing primitives","\u002Fhtml-css\u002Fcss-functions-sizing","12.html-css\u002F25.css-functions-sizing",{"title":3238,"path":3239,"stem":3240},"Rendering Pipeline і CSS Performance","\u002Fhtml-css\u002Fcss-rendering-performance","12.html-css\u002F26.css-rendering-performance",{"title":3242,"path":3243,"stem":3244},"CSS Best Practices: типові ситуації та правильні рішення","\u002Fhtml-css\u002Fcss-best-practices","12.html-css\u002F27.css-best-practices",{"title":3246,"path":3247,"stem":3248,"children":3249,"page":59},"AWS","\u002Faws","13.aws",[3250,3254,3258,3262,3266,3270,3274,3278,3282,3286,3290,3294,3298,3302,3306,3310,3314,3318],{"title":3251,"path":3252,"stem":3253},"Реєстрація AWS акаунту та студентські програми","\u002Faws\u002Faccount-registration","13.aws\u002F00.account-registration",{"title":3255,"path":3256,"stem":3257},"Вступ до хмарних обчислень та AWS","\u002Faws\u002Fintroduction-to-cloud","13.aws\u002F01.introduction-to-cloud",{"title":3259,"path":3260,"stem":3261},"AWS IAM — Identity and Access Management","\u002Faws\u002Fiam","13.aws\u002F02.iam",{"title":3263,"path":3264,"stem":3265},"AWS IAM CLI — Довідник команд","\u002Faws\u002F02a.iam-doc","13.aws\u002F02a.iam-doc",{"title":3267,"path":3268,"stem":3269},"Docker та контейнеризація в AWS — ECR, ECS та Fargate","\u002Faws\u002Fdocker-ecs","13.aws\u002F03.docker-ecs",{"title":3271,"path":3272,"stem":3273},"AWS ECR \u002F ECS CLI — Довідник команд","\u002Faws\u002F03a.docker-ecs-doc","13.aws\u002F03a.docker-ecs-doc",{"title":3275,"path":3276,"stem":3277},"Amazon EC2 — Elastic Compute Cloud","\u002Faws\u002Fec2","13.aws\u002F04.ec2",{"title":3279,"path":3280,"stem":3281},"AWS EC2 CLI — Довідник команд","\u002Faws\u002F04a.ec2-doc","13.aws\u002F04a.ec2-doc",{"title":3283,"path":3284,"stem":3285},"Elastic Load Balancing та Auto Scaling","\u002Faws\u002Falb-asg","13.aws\u002F05.alb-asg",{"title":3287,"path":3288,"stem":3289},"Amazon S3 — Simple Storage Service","\u002Faws\u002Fs3","13.aws\u002F06.s3",{"title":3291,"path":3292,"stem":3293},"Amazon CloudFront — Content Delivery Network","\u002Faws\u002Fcloudfront","13.aws\u002F07.cloudfront",{"title":3295,"path":3296,"stem":3297},"Amazon RDS — Relational Database Service","\u002Faws\u002Frds","13.aws\u002F08.rds",{"title":3299,"path":3300,"stem":3301},"Amazon DynamoDB — NoSQL Database","\u002Faws\u002Fdynamodb","13.aws\u002F09.dynamodb",{"title":3303,"path":3304,"stem":3305},"AWS Lambda та Serverless Compute","\u002Faws\u002Flambda","13.aws\u002F10.lambda",{"title":3307,"path":3308,"stem":3309},"Amazon Bedrock - Foundation Models, RAG та Agents","\u002Faws\u002Fbedrock","13.aws\u002F22.bedrock",{"title":3311,"path":3312,"stem":3313},"Amazon Rekognition - Комп'ютерний зір","\u002Faws\u002Frekognition","13.aws\u002F23.rekognition",{"title":3315,"path":3316,"stem":3317},"Amazon Textract - Інтелектуальний аналіз документів","\u002Faws\u002Ftextract","13.aws\u002F24.textract",{"title":3319,"path":3320,"stem":3321},"Amazon Polly, Transcribe, Comprehend та Translate","\u002Faws\u002Faudio-nlp-services","13.aws\u002F25.audio-nlp-services",{"title":3323,"path":3324,"stem":3325,"children":3326,"page":59},"Tailwind","\u002Ftailwind","21.tailwind",[3327,3331,3335,3339,3343,3347,3351,3355,3359,3363,3367,3371],{"title":3328,"path":3329,"stem":3330},"Що таке Tailwind CSS і навіщо він потрібен","\u002Ftailwind\u002Ftailwind-intro-philosophy","21.tailwind\u002F01.tailwind-intro-philosophy",{"title":3332,"path":3333,"stem":3334},"Встановлення та налаштування Tailwind CSS v4","\u002Ftailwind\u002Ftailwind-installation-setup","21.tailwind\u002F02.tailwind-installation-setup",{"title":3336,"path":3337,"stem":3338},"Utility-класи: основи та система Tailwind","\u002Ftailwind\u002Ftailwind-utility-classes-core","21.tailwind\u002F03.tailwind-utility-classes-core",{"title":3340,"path":3341,"stem":3342},"Layout: Flexbox та Grid через Tailwind","\u002Ftailwind\u002Ftailwind-flexbox-grid","21.tailwind\u002F04.tailwind-flexbox-grid",{"title":3344,"path":3345,"stem":3346},"Кастомізація теми через @theme у Tailwind v4","\u002Ftailwind\u002Ftailwind-theme-customization","21.tailwind\u002F05.tailwind-theme-customization",{"title":3348,"path":3349,"stem":3350},"Варіанти: hover, focus, responsive, dark mode та нові v4","\u002Ftailwind\u002Ftailwind-variants-states","21.tailwind\u002F06.tailwind-variants-states",{"title":3352,"path":3353,"stem":3354},"Типографіка та система кольорів у Tailwind v4","\u002Ftailwind\u002Ftailwind-typography-colors","21.tailwind\u002F07.tailwind-typography-colors",{"title":3356,"path":3357,"stem":3358},"Компоненти та повторюваність: @apply, @utility та патерни","\u002Ftailwind\u002Ftailwind-components-patterns","21.tailwind\u002F08.tailwind-components-patterns",{"title":3360,"path":3361,"stem":3362},"Темна тема та система дизайн-токенів у Tailwind v4","\u002Ftailwind\u002Ftailwind-dark-mode-theming","21.tailwind\u002F09.tailwind-dark-mode-theming",{"title":3364,"path":3365,"stem":3366},"Довільні значення та контейнерні запити у Tailwind v4","\u002Ftailwind\u002Ftailwind-arbitrary-container-queries","21.tailwind\u002F10.tailwind-arbitrary-container-queries",{"title":3368,"path":3369,"stem":3370},"Анімації, трансформації та 3D у Tailwind v4","\u002Ftailwind\u002Ftailwind-animations-transforms","21.tailwind\u002F11.tailwind-animations-transforms",{"title":3372,"path":3373,"stem":3374},"Tailwind CLI, PostCSS та інтеграція з фреймворками","\u002Ftailwind\u002Ftailwind-cli-tooling","21.tailwind\u002F12.tailwind-cli-tooling",{"title":3376,"path":3377,"stem":3378},"Тестування компонентів діаграм","\u002Ftest-components","98.test-components",{"id":3380,"title":2561,"body":3381,"description":21674,"extension":21675,"links":21676,"meta":21677,"navigation":3451,"path":2562,"seo":21678,"stem":2563,"__hash__":21679},"docs\u002F05.python\u002F02.encapsulation.md",{"type":3382,"value":3383,"toc":21614},"minimark",[3384,3389,3394,3407,3512,3520,3552,3555,3559,3564,3571,3583,3590,3689,3691,3695,3702,3709,3816,3823,3834,4137,4188,4230,4260,4262,4269,4276,4280,4291,4580,4641,4656,4658,4662,4665,4907,4951,4965,4967,4971,5078,5080,5088,5092,5103,5268,5280,5343,5345,5352,5361,5779,5829,5831,5835,5850,6357,6677,6760,6762,6766,6769,6881,6907,6919,7010,7012,7019,7340,7375,7380,7402,7404,7408,7412,7428,7474,7478,7492,7588,7755,7781,7785,7928,7935,7941,8647,8677,8687,8713,8863,8898,8904,8906,8910,8916,8919,9189,9195,9202,9205,9511,9519,10747,10843,10876,10878,10882,10886,10894,11764,11856,11858,11862,11868,14016,14125,14127,14131,14138,15771,15870,15872,15876,15883,16001,16005,16319,16323,21144,21525,21527,21531,21610],[3385,3386,3388],"h1",{"id":3387},"інкапсуляція-та-керування-доступом","Інкапсуляція та керування доступом",[3390,3391,3393],"h2",{"id":3392},"вступ-коли-відкритий-клас-стає-небезпечним","Вступ: коли «відкритий» клас стає небезпечним",[3395,3396,3397,3398,3402,3403,3406],"p",{},"Уявіть банківський сервіс. Клас ",[3399,3400,3401],"code",{},"Account"," зберігає баланс у публічному атрибуті ",[3399,3404,3405],{},"balance",". Перший день у production:",[3408,3409,3414],"pre",{"className":3410,"code":3411,"language":3412,"meta":3413,"style":3413},"language-python shiki shiki-themes light-plus dark-plus dark-plus","# ❌ Катастрофа в production\naccount = Account(\"Олена\", 50_000.0)\n\n# Хтось з команди вирішив «швидко» виправити баг:\naccount.balance = -999_999.0   # від'ємний баланс — фінансовий крах\naccount.balance = \"заморожено\"  # str замість float — AttributeError о 3 ночі\n\n# Або підрядник отримав доступ до внутрішнього кешу:\naccount._cache[\"approved_limit\"] = 10_000_000  # обхід перевірок!\n","python","",[3399,3415,3416,3425,3446,3453,3459,3471,3483,3488,3494],{"__ignoreMap":3413},[3417,3418,3421],"span",{"class":3419,"line":3420},"line",1,[3417,3422,3424],{"class":3423},"spJ8K","# ❌ Катастрофа в production\n",[3417,3426,3428,3432,3436,3439,3443],{"class":3419,"line":3427},2,[3417,3429,3431],{"class":3430},"sHH4Y","account = Account(",[3417,3433,3435],{"class":3434},"sbdoH","\"Олена\"",[3417,3437,3438],{"class":3430},", ",[3417,3440,3442],{"class":3441},"sJj4R","50_000.0",[3417,3444,3445],{"class":3430},")\n",[3417,3447,3449],{"class":3419,"line":3448},3,[3417,3450,3452],{"emptyLinePlaceholder":3451},true,"\n",[3417,3454,3456],{"class":3419,"line":3455},4,[3417,3457,3458],{"class":3423},"# Хтось з команди вирішив «швидко» виправити баг:\n",[3417,3460,3462,3465,3468],{"class":3419,"line":3461},5,[3417,3463,3464],{"class":3430},"account.balance = -",[3417,3466,3467],{"class":3441},"999_999.0",[3417,3469,3470],{"class":3423},"   # від'ємний баланс — фінансовий крах\n",[3417,3472,3474,3477,3480],{"class":3419,"line":3473},6,[3417,3475,3476],{"class":3430},"account.balance = ",[3417,3478,3479],{"class":3434},"\"заморожено\"",[3417,3481,3482],{"class":3423},"  # str замість float — AttributeError о 3 ночі\n",[3417,3484,3486],{"class":3419,"line":3485},7,[3417,3487,3452],{"emptyLinePlaceholder":3451},[3417,3489,3491],{"class":3419,"line":3490},8,[3417,3492,3493],{"class":3423},"# Або підрядник отримав доступ до внутрішнього кешу:\n",[3417,3495,3497,3500,3503,3506,3509],{"class":3419,"line":3496},9,[3417,3498,3499],{"class":3430},"account._cache[",[3417,3501,3502],{"class":3434},"\"approved_limit\"",[3417,3504,3505],{"class":3430},"] = ",[3417,3507,3508],{"class":3441},"10_000_000",[3417,3510,3511],{"class":3423},"  # обхід перевірок!\n",[3395,3513,3514,3515,3519],{},"Жодного з цих сценаріїв не було б, якби клас ",[3516,3517,3518],"strong",{},"контролював доступ до своїх даних",". Саме це і вирішує інкапсуляція.",[3521,3522,3523,3529,3542,3547],"card-group",{},[3524,3525,3528],"card",{"icon":3526,"title":3527},"i-heroicons-shield-check","Захист даних","Публічний інтерфейс (методи та властивості) контролює всі зміни внутрішнього стану. Невалідні значення відхиляються до того, як вони потраплять у пам'ять.",[3524,3530,3533,3534,3537,3538,3541],{"icon":3531,"title":3532},"i-heroicons-arrow-path","Стабільний API","Внутрішня реалізація може змінюватися — переїзд з ",[3399,3535,3536],{},"float"," до ",[3399,3539,3540],{},"Decimal",", зміна структури кешу. Зовнішній код цього не відчує, бо взаємодіє лише через публічний інтерфейс.",[3524,3543,3546],{"icon":3544,"title":3545},"i-heroicons-link-slash","Менше зв'язності","Коли код взаємодіє через чітко визначений API — зміна одного класу не ламає десятки інших. Зв'язність (coupling) мінімальна.",[3524,3548,3551],{"icon":3549,"title":3550},"i-heroicons-document-text","Самодокументованість","Один погляд на публічний інтерфейс класу — і зрозуміло, що він вміє. Приватні деталі приховані і не захаращують API.",[3553,3554],"hr",{},[3390,3556,3558],{"id":3557},"частина-i-філософія-доступу-в-python","Частина I: Філософія доступу в Python",[3560,3561,3563],"h3",{"id":3562},"ми-всі-тут-дорослі-люди","«Ми всі тут дорослі люди»",[3395,3565,3566,3567,3570],{},"У C++, Java, C# компілятор фізично забороняє доступ до ",[3399,3568,3569],{},"private","-членів — код просто не скомпілюється. Python обрав інший шлях. Творець мови Гвідо ван Россум сформулював принцип:",[3572,3573,3574],"blockquote",{},[3395,3575,3576,3579],{},[3516,3577,3578],{},"«We are all consenting adults here»",[3580,3581,3582],"em",{},"(«Ми всі тут дорослі люди, що діють за згодою»)",[3395,3584,3585,3586,3589],{},"Мова не ставить жорстких бар'єрів. Якщо розробник хоче залізти у внутрішні деталі класу — він може. Але відповідальність за зламаний код повністю на ньому. Замість заборон Python використовує ",[3516,3587,3588],{},"угоди про іменування (naming conventions)",".",[3591,3592,3593],"plant-uml",{},[3408,3594,3598],{"className":3595,"code":3596,"language":3597,"meta":3413,"style":3413},"language-plantuml shiki shiki-themes light-plus dark-plus dark-plus","@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam ArrowColor #6366f1\n\npackage \"Об'єкт класу (Екземпляр)\" #f3f4f6 {\n    rectangle \"Внутрішній стан\\n[_balance, __secret, _cache]\\n(Деталі реалізації)\" as Internal #fee2e2\n    rectangle \"Публічний інтерфейс (API)\\n[deposit(), withdraw(), balance]\" as Public #d1fae5\n\n    Public -down-> Internal : \"безпечно модифікує\\nта зчитує\"\n}\n\nactor \"Зовнішній код\" as Client\n\nClient -right-> Public : \"1. Викликає методи ✅\"\nClient --x Internal : \"2. Прямий доступ ❌\\n(порушення угоди)\"\n@enduml\n","plantuml",[3399,3599,3600,3605,3610,3615,3620,3624,3629,3634,3639,3643,3649,3655,3660,3666,3671,3677,3683],{"__ignoreMap":3413},[3417,3601,3602],{"class":3419,"line":3420},[3417,3603,3604],{},"@startuml\n",[3417,3606,3607],{"class":3419,"line":3427},[3417,3608,3609],{},"skinparam style plain\n",[3417,3611,3612],{"class":3419,"line":3448},[3417,3613,3614],{},"skinparam backgroundColor #ffffff\n",[3417,3616,3617],{"class":3419,"line":3455},[3417,3618,3619],{},"skinparam ArrowColor #6366f1\n",[3417,3621,3622],{"class":3419,"line":3461},[3417,3623,3452],{"emptyLinePlaceholder":3451},[3417,3625,3626],{"class":3419,"line":3473},[3417,3627,3628],{},"package \"Об'єкт класу (Екземпляр)\" #f3f4f6 {\n",[3417,3630,3631],{"class":3419,"line":3485},[3417,3632,3633],{},"    rectangle \"Внутрішній стан\\n[_balance, __secret, _cache]\\n(Деталі реалізації)\" as Internal #fee2e2\n",[3417,3635,3636],{"class":3419,"line":3490},[3417,3637,3638],{},"    rectangle \"Публічний інтерфейс (API)\\n[deposit(), withdraw(), balance]\" as Public #d1fae5\n",[3417,3640,3641],{"class":3419,"line":3496},[3417,3642,3452],{"emptyLinePlaceholder":3451},[3417,3644,3646],{"class":3419,"line":3645},10,[3417,3647,3648],{},"    Public -down-> Internal : \"безпечно модифікує\\nта зчитує\"\n",[3417,3650,3652],{"class":3419,"line":3651},11,[3417,3653,3654],{},"}\n",[3417,3656,3658],{"class":3419,"line":3657},12,[3417,3659,3452],{"emptyLinePlaceholder":3451},[3417,3661,3663],{"class":3419,"line":3662},13,[3417,3664,3665],{},"actor \"Зовнішній код\" as Client\n",[3417,3667,3669],{"class":3419,"line":3668},14,[3417,3670,3452],{"emptyLinePlaceholder":3451},[3417,3672,3674],{"class":3419,"line":3673},15,[3417,3675,3676],{},"Client -right-> Public : \"1. Викликає методи ✅\"\n",[3417,3678,3680],{"class":3419,"line":3679},16,[3417,3681,3682],{},"Client --x Internal : \"2. Прямий доступ ❌\\n(порушення угоди)\"\n",[3417,3684,3686],{"class":3419,"line":3685},17,[3417,3687,3688],{},"@enduml\n",[3553,3690],{},[3390,3692,3694],{"id":3693},"частина-ii-публічні-захищені-та-приватні-атрибути","Частина II: Публічні, захищені та приватні атрибути",[3560,3696,3698,3701],{"id":3697},"public-за-замовчуванням",[3399,3699,3700],{},"public"," — за замовчуванням",[3395,3703,3704,3705,3708],{},"Усі імена в класі без підкреслень є ",[3516,3706,3707],{},"публічними",". Вони є частиною стабільного публічного API і можуть змінюватись ззовні.",[3408,3710,3712],{"className":3410,"code":3711,"language":3412,"meta":3413,"style":3413},"class Point:\n    def __init__(self, x: float, y: float):\n        self.x = x  # публічний — змінювати можна і потрібно\n        self.y = y  # публічний\n\np = Point(3.0, 4.0)\np.x = 10.0  # ✅ цілком нормально\n",[3399,3713,3714,3727,3765,3776,3786,3790,3805],{"__ignoreMap":3413},[3417,3715,3716,3720,3724],{"class":3419,"line":3420},[3417,3717,3719],{"class":3718},"su1O8","class",[3417,3721,3723],{"class":3722},"sN1BT"," Point",[3417,3725,3726],{"class":3430},":\n",[3417,3728,3729,3732,3736,3739,3743,3745,3748,3751,3753,3755,3758,3760,3762],{"class":3419,"line":3427},[3417,3730,3731],{"class":3718},"    def",[3417,3733,3735],{"class":3734},"s8Opu"," __init__",[3417,3737,3738],{"class":3430},"(",[3417,3740,3742],{"class":3741},"siwwj","self",[3417,3744,3438],{"class":3430},[3417,3746,3747],{"class":3741},"x",[3417,3749,3750],{"class":3430},": ",[3417,3752,3536],{"class":3722},[3417,3754,3438],{"class":3430},[3417,3756,3757],{"class":3741},"y",[3417,3759,3750],{"class":3430},[3417,3761,3536],{"class":3722},[3417,3763,3764],{"class":3430},"):\n",[3417,3766,3767,3770,3773],{"class":3419,"line":3448},[3417,3768,3769],{"class":3718},"        self",[3417,3771,3772],{"class":3430},".x = x  ",[3417,3774,3775],{"class":3423},"# публічний — змінювати можна і потрібно\n",[3417,3777,3778,3780,3783],{"class":3419,"line":3455},[3417,3779,3769],{"class":3718},[3417,3781,3782],{"class":3430},".y = y  ",[3417,3784,3785],{"class":3423},"# публічний\n",[3417,3787,3788],{"class":3419,"line":3461},[3417,3789,3452],{"emptyLinePlaceholder":3451},[3417,3791,3792,3795,3798,3800,3803],{"class":3419,"line":3473},[3417,3793,3794],{"class":3430},"p = Point(",[3417,3796,3797],{"class":3441},"3.0",[3417,3799,3438],{"class":3430},[3417,3801,3802],{"class":3441},"4.0",[3417,3804,3445],{"class":3430},[3417,3806,3807,3810,3813],{"class":3419,"line":3485},[3417,3808,3809],{"class":3430},"p.x = ",[3417,3811,3812],{"class":3441},"10.0",[3417,3814,3815],{"class":3423},"  # ✅ цілком нормально\n",[3560,3817,3819,3822],{"id":3818},"_protected-угода-про-внутрішнє-використання",[3399,3820,3821],{},"_protected"," — угода про внутрішнє використання",[3395,3824,3825,3826,3829,3830,3833],{},"Один символ підкреслення перед іменем — ",[3516,3827,3828],{},"сигнал",": «це деталь реалізації, не призначена для прямого використання ззовні». Python це ",[3516,3831,3832],{},"не забороняє",", але весь інструментарій — IDE, linters, рецензенти коду — реагує на це попередженням.",[3408,3835,3837],{"className":3410,"code":3836,"language":3412,"meta":3413,"style":3413},"# bank_account.py\n\nclass BankAccount:\n    def __init__(self, owner: str, initial_balance: float):\n        self.owner = owner               # публічний ✅\n        self._balance = initial_balance  # захищений — для внутрішнього використання\n        self._transaction_log: list = [] # захищений — деталь реалізації\n\n    def deposit(self, amount: float) -> None:\n        if amount \u003C= 0:\n            raise ValueError(\"Сума депозиту має бути додатною\")\n        self._balance += amount\n        self._transaction_log.append(f\"+{amount}\")\n\n    def withdraw(self, amount: float) -> None:\n        if amount > self._balance:\n            raise ValueError(\"Недостатньо коштів\")\n        self._balance -= amount\n        self._transaction_log.append(f\"-{amount}\")\n\n    def get_balance(self) -> float:\n        return self._balance\n",[3399,3838,3839,3844,3848,3857,3888,3898,3908,3924,3928,3956,3970,3985,3992,4018,4022,4047,4059,4072,4080,4102,4107,4125],{"__ignoreMap":3413},[3417,3840,3841],{"class":3419,"line":3420},[3417,3842,3843],{"class":3423},"# bank_account.py\n",[3417,3845,3846],{"class":3419,"line":3427},[3417,3847,3452],{"emptyLinePlaceholder":3451},[3417,3849,3850,3852,3855],{"class":3419,"line":3448},[3417,3851,3719],{"class":3718},[3417,3853,3854],{"class":3722}," BankAccount",[3417,3856,3726],{"class":3430},[3417,3858,3859,3861,3863,3865,3867,3869,3872,3874,3877,3879,3882,3884,3886],{"class":3419,"line":3455},[3417,3860,3731],{"class":3718},[3417,3862,3735],{"class":3734},[3417,3864,3738],{"class":3430},[3417,3866,3742],{"class":3741},[3417,3868,3438],{"class":3430},[3417,3870,3871],{"class":3741},"owner",[3417,3873,3750],{"class":3430},[3417,3875,3876],{"class":3722},"str",[3417,3878,3438],{"class":3430},[3417,3880,3881],{"class":3741},"initial_balance",[3417,3883,3750],{"class":3430},[3417,3885,3536],{"class":3722},[3417,3887,3764],{"class":3430},[3417,3889,3890,3892,3895],{"class":3419,"line":3461},[3417,3891,3769],{"class":3718},[3417,3893,3894],{"class":3430},".owner = owner               ",[3417,3896,3897],{"class":3423},"# публічний ✅\n",[3417,3899,3900,3902,3905],{"class":3419,"line":3473},[3417,3901,3769],{"class":3718},[3417,3903,3904],{"class":3430},"._balance = initial_balance  ",[3417,3906,3907],{"class":3423},"# захищений — для внутрішнього використання\n",[3417,3909,3910,3912,3915,3918,3921],{"class":3419,"line":3485},[3417,3911,3769],{"class":3718},[3417,3913,3914],{"class":3430},"._transaction_log: ",[3417,3916,3917],{"class":3722},"list",[3417,3919,3920],{"class":3430}," = [] ",[3417,3922,3923],{"class":3423},"# захищений — деталь реалізації\n",[3417,3925,3926],{"class":3419,"line":3490},[3417,3927,3452],{"emptyLinePlaceholder":3451},[3417,3929,3930,3932,3935,3937,3939,3941,3944,3946,3948,3951,3954],{"class":3419,"line":3496},[3417,3931,3731],{"class":3718},[3417,3933,3934],{"class":3734}," deposit",[3417,3936,3738],{"class":3430},[3417,3938,3742],{"class":3741},[3417,3940,3438],{"class":3430},[3417,3942,3943],{"class":3741},"amount",[3417,3945,3750],{"class":3430},[3417,3947,3536],{"class":3722},[3417,3949,3950],{"class":3430},") -> ",[3417,3952,3953],{"class":3718},"None",[3417,3955,3726],{"class":3430},[3417,3957,3958,3962,3965,3968],{"class":3419,"line":3645},[3417,3959,3961],{"class":3960},"s8xlr","        if",[3417,3963,3964],{"class":3430}," amount \u003C= ",[3417,3966,3967],{"class":3441},"0",[3417,3969,3726],{"class":3430},[3417,3971,3972,3975,3978,3980,3983],{"class":3419,"line":3651},[3417,3973,3974],{"class":3960},"            raise",[3417,3976,3977],{"class":3722}," ValueError",[3417,3979,3738],{"class":3430},[3417,3981,3982],{"class":3434},"\"Сума депозиту має бути додатною\"",[3417,3984,3445],{"class":3430},[3417,3986,3987,3989],{"class":3419,"line":3657},[3417,3988,3769],{"class":3718},[3417,3990,3991],{"class":3430},"._balance += amount\n",[3417,3993,3994,3996,3999,4002,4005,4008,4010,4013,4016],{"class":3419,"line":3662},[3417,3995,3769],{"class":3718},[3417,3997,3998],{"class":3430},"._transaction_log.append(",[3417,4000,4001],{"class":3718},"f",[3417,4003,4004],{"class":3434},"\"+",[3417,4006,4007],{"class":3718},"{",[3417,4009,3943],{"class":3430},[3417,4011,4012],{"class":3718},"}",[3417,4014,4015],{"class":3434},"\"",[3417,4017,3445],{"class":3430},[3417,4019,4020],{"class":3419,"line":3668},[3417,4021,3452],{"emptyLinePlaceholder":3451},[3417,4023,4024,4026,4029,4031,4033,4035,4037,4039,4041,4043,4045],{"class":3419,"line":3673},[3417,4025,3731],{"class":3718},[3417,4027,4028],{"class":3734}," withdraw",[3417,4030,3738],{"class":3430},[3417,4032,3742],{"class":3741},[3417,4034,3438],{"class":3430},[3417,4036,3943],{"class":3741},[3417,4038,3750],{"class":3430},[3417,4040,3536],{"class":3722},[3417,4042,3950],{"class":3430},[3417,4044,3953],{"class":3718},[3417,4046,3726],{"class":3430},[3417,4048,4049,4051,4054,4056],{"class":3419,"line":3679},[3417,4050,3961],{"class":3960},[3417,4052,4053],{"class":3430}," amount > ",[3417,4055,3742],{"class":3718},[3417,4057,4058],{"class":3430},"._balance:\n",[3417,4060,4061,4063,4065,4067,4070],{"class":3419,"line":3685},[3417,4062,3974],{"class":3960},[3417,4064,3977],{"class":3722},[3417,4066,3738],{"class":3430},[3417,4068,4069],{"class":3434},"\"Недостатньо коштів\"",[3417,4071,3445],{"class":3430},[3417,4073,4075,4077],{"class":3419,"line":4074},18,[3417,4076,3769],{"class":3718},[3417,4078,4079],{"class":3430},"._balance -= amount\n",[3417,4081,4083,4085,4087,4089,4092,4094,4096,4098,4100],{"class":3419,"line":4082},19,[3417,4084,3769],{"class":3718},[3417,4086,3998],{"class":3430},[3417,4088,4001],{"class":3718},[3417,4090,4091],{"class":3434},"\"-",[3417,4093,4007],{"class":3718},[3417,4095,3943],{"class":3430},[3417,4097,4012],{"class":3718},[3417,4099,4015],{"class":3434},[3417,4101,3445],{"class":3430},[3417,4103,4105],{"class":3419,"line":4104},20,[3417,4106,3452],{"emptyLinePlaceholder":3451},[3417,4108,4110,4112,4115,4117,4119,4121,4123],{"class":3419,"line":4109},21,[3417,4111,3731],{"class":3718},[3417,4113,4114],{"class":3734}," get_balance",[3417,4116,3738],{"class":3430},[3417,4118,3742],{"class":3741},[3417,4120,3950],{"class":3430},[3417,4122,3536],{"class":3722},[3417,4124,3726],{"class":3430},[3417,4126,4128,4131,4134],{"class":3419,"line":4127},22,[3417,4129,4130],{"class":3960},"        return",[3417,4132,4133],{"class":3718}," self",[3417,4135,4136],{"class":3430},"._balance\n",[3408,4138,4140],{"className":3410,"code":4139,"language":3412,"meta":3413,"style":3413},"account = BankAccount(\"Денис\", 1000.0)\n\n# ⚠️ Технічно працює, але порушує угоду!\nprint(account._balance)       # 1000.0 — IDE покаже попередження\naccount._balance = -9999.0    # Баланс зламано — без жодних перевірок!\n",[3399,4141,4142,4157,4161,4166,4177],{"__ignoreMap":3413},[3417,4143,4144,4147,4150,4152,4155],{"class":3419,"line":3420},[3417,4145,4146],{"class":3430},"account = BankAccount(",[3417,4148,4149],{"class":3434},"\"Денис\"",[3417,4151,3438],{"class":3430},[3417,4153,4154],{"class":3441},"1000.0",[3417,4156,3445],{"class":3430},[3417,4158,4159],{"class":3419,"line":3427},[3417,4160,3452],{"emptyLinePlaceholder":3451},[3417,4162,4163],{"class":3419,"line":3448},[3417,4164,4165],{"class":3423},"# ⚠️ Технічно працює, але порушує угоду!\n",[3417,4167,4168,4171,4174],{"class":3419,"line":3455},[3417,4169,4170],{"class":3734},"print",[3417,4172,4173],{"class":3430},"(account._balance)       ",[3417,4175,4176],{"class":3423},"# 1000.0 — IDE покаже попередження\n",[3417,4178,4179,4182,4185],{"class":3419,"line":3461},[3417,4180,4181],{"class":3430},"account._balance = -",[3417,4183,4184],{"class":3441},"9999.0",[3417,4186,4187],{"class":3423},"    # Баланс зламано — без жодних перевірок!\n",[4189,4190,4192,4205,4220],"terminal-preview",{"title":4191},"pylint bank_account.py",[4193,4194,4196,4201,4202],"div",{"className":4195},[3419],[3417,4197,4200],{"className":4198},[4199],"opacity-40","$"," ",[3516,4203,4204],{},"pylint --disable=all --enable=W0212 main.py",[4193,4206,4208,4209,4214,4215],{"className":4207},[3419],"main.py:8:6: ",[3417,4210,4213],{"className":4211},[4212],"text-yellow-400","W0212",": Access to a protected member _balance of a client class ",[3417,4216,4219],{"className":4217},[4218],"text-gray-400","(protected-access)",[4193,4221,4223,4224,4214,4227],{"className":4222},[3419],"main.py:9:0: ",[3417,4225,4213],{"className":4226},[4212],[3417,4228,4219],{"className":4229},[4218],[3521,4231,4232,4240,4252],{},[3524,4233,4236,4237,4239],{"icon":4234,"title":4235},"i-heroicons-code-bracket-square","Підтримка IDE","VS Code і PyCharm приховують ",[3399,4238,3821],{}," атрибути в автодоповненні або виділяють їх попередженням при зверненні ззовні класу.",[3524,4241,4244,4247,4248,4251],{"icon":4242,"title":4243},"i-heroicons-shield-exclamation","Статичні лінтери",[3399,4245,4246],{},"pylint"," (W0212), ",[3399,4249,4250],{},"flake8-bugbear"," та інші видають попередження при доступі до захищених членів ззовні їхньої ієрархії.",[3524,4253,4255,4256,4259],{"icon":3531,"title":4254},"Нестабільний API","Атрибут з ",[3399,4257,4258],{},"_"," може бути перейменований або видалений у будь-якому мінорному релізі бібліотеки. Використовуючи його — ви приймаєте ризик поломки при оновленні.",[3553,4261],{},[3560,4263,4265,4268],{"id":4264},"__private-name-mangling",[3399,4266,4267],{},"__private"," — Name Mangling",[3395,4270,4271,4272,4275],{},"Два підкреслення перед іменем активують механізм ",[3516,4273,4274],{},"викривлення імен (Name Mangling)",". Компілятор Python автоматично перейменовує такий атрибут за шаблоном:",[4277,4278,4279],"math-formula",{},"_ClassName__attribute",[3395,4281,4282,4283,4286,4287,4290],{},"Це ",[3516,4284,4285],{},"не шифрування"," і ",[3516,4288,4289],{},"не справжня приватність"," — це лише захист від випадкових колізій імен.",[3408,4292,4294],{"className":3410,"code":4293,"language":3412,"meta":3413,"style":3413},"# secure_wallet.py\n\nclass SecureWallet:\n    def __init__(self, owner: str, initial_funds: float):\n        self.owner = owner\n        self.__funds = initial_funds  # → _SecureWallet__funds у пам'яті\n\n    def add_funds(self, amount: float) -> None:\n        if amount > 0:\n            self.__funds += amount\n\n    def get_funds(self) -> float:\n        return self.__funds\n\n\nwallet = SecureWallet(\"Марія\", 500.0)\n\n# Прямий доступ — AttributeError\ntry:\n    print(wallet.__funds)\nexcept AttributeError as e:\n    print(f\"AttributeError: {e}\")\n\n# Досліджуємо __dict__ — бачимо викривлене ім'я\nprint(wallet.__dict__)\n\n# Доступ через викривлене ім'я — все ще можливий!\nprint(wallet._SecureWallet__funds)\nwallet._SecureWallet__funds = -1000.0\nprint(wallet.get_funds())\n",[3399,4295,4296,4301,4305,4314,4343,4350,4360,4364,4389,4399,4407,4411,4428,4437,4441,4445,4460,4464,4469,4476,4484,4498,4520,4525,4531,4544,4549,4555,4563,4572],{"__ignoreMap":3413},[3417,4297,4298],{"class":3419,"line":3420},[3417,4299,4300],{"class":3423},"# secure_wallet.py\n",[3417,4302,4303],{"class":3419,"line":3427},[3417,4304,3452],{"emptyLinePlaceholder":3451},[3417,4306,4307,4309,4312],{"class":3419,"line":3448},[3417,4308,3719],{"class":3718},[3417,4310,4311],{"class":3722}," SecureWallet",[3417,4313,3726],{"class":3430},[3417,4315,4316,4318,4320,4322,4324,4326,4328,4330,4332,4334,4337,4339,4341],{"class":3419,"line":3455},[3417,4317,3731],{"class":3718},[3417,4319,3735],{"class":3734},[3417,4321,3738],{"class":3430},[3417,4323,3742],{"class":3741},[3417,4325,3438],{"class":3430},[3417,4327,3871],{"class":3741},[3417,4329,3750],{"class":3430},[3417,4331,3876],{"class":3722},[3417,4333,3438],{"class":3430},[3417,4335,4336],{"class":3741},"initial_funds",[3417,4338,3750],{"class":3430},[3417,4340,3536],{"class":3722},[3417,4342,3764],{"class":3430},[3417,4344,4345,4347],{"class":3419,"line":3461},[3417,4346,3769],{"class":3718},[3417,4348,4349],{"class":3430},".owner = owner\n",[3417,4351,4352,4354,4357],{"class":3419,"line":3473},[3417,4353,3769],{"class":3718},[3417,4355,4356],{"class":3430},".__funds = initial_funds  ",[3417,4358,4359],{"class":3423},"# → _SecureWallet__funds у пам'яті\n",[3417,4361,4362],{"class":3419,"line":3485},[3417,4363,3452],{"emptyLinePlaceholder":3451},[3417,4365,4366,4368,4371,4373,4375,4377,4379,4381,4383,4385,4387],{"class":3419,"line":3490},[3417,4367,3731],{"class":3718},[3417,4369,4370],{"class":3734}," add_funds",[3417,4372,3738],{"class":3430},[3417,4374,3742],{"class":3741},[3417,4376,3438],{"class":3430},[3417,4378,3943],{"class":3741},[3417,4380,3750],{"class":3430},[3417,4382,3536],{"class":3722},[3417,4384,3950],{"class":3430},[3417,4386,3953],{"class":3718},[3417,4388,3726],{"class":3430},[3417,4390,4391,4393,4395,4397],{"class":3419,"line":3496},[3417,4392,3961],{"class":3960},[3417,4394,4053],{"class":3430},[3417,4396,3967],{"class":3441},[3417,4398,3726],{"class":3430},[3417,4400,4401,4404],{"class":3419,"line":3645},[3417,4402,4403],{"class":3718},"            self",[3417,4405,4406],{"class":3430},".__funds += amount\n",[3417,4408,4409],{"class":3419,"line":3651},[3417,4410,3452],{"emptyLinePlaceholder":3451},[3417,4412,4413,4415,4418,4420,4422,4424,4426],{"class":3419,"line":3657},[3417,4414,3731],{"class":3718},[3417,4416,4417],{"class":3734}," get_funds",[3417,4419,3738],{"class":3430},[3417,4421,3742],{"class":3741},[3417,4423,3950],{"class":3430},[3417,4425,3536],{"class":3722},[3417,4427,3726],{"class":3430},[3417,4429,4430,4432,4434],{"class":3419,"line":3662},[3417,4431,4130],{"class":3960},[3417,4433,4133],{"class":3718},[3417,4435,4436],{"class":3430},".__funds\n",[3417,4438,4439],{"class":3419,"line":3668},[3417,4440,3452],{"emptyLinePlaceholder":3451},[3417,4442,4443],{"class":3419,"line":3673},[3417,4444,3452],{"emptyLinePlaceholder":3451},[3417,4446,4447,4450,4453,4455,4458],{"class":3419,"line":3679},[3417,4448,4449],{"class":3430},"wallet = SecureWallet(",[3417,4451,4452],{"class":3434},"\"Марія\"",[3417,4454,3438],{"class":3430},[3417,4456,4457],{"class":3441},"500.0",[3417,4459,3445],{"class":3430},[3417,4461,4462],{"class":3419,"line":3685},[3417,4463,3452],{"emptyLinePlaceholder":3451},[3417,4465,4466],{"class":3419,"line":4074},[3417,4467,4468],{"class":3423},"# Прямий доступ — AttributeError\n",[3417,4470,4471,4474],{"class":3419,"line":4082},[3417,4472,4473],{"class":3960},"try",[3417,4475,3726],{"class":3430},[3417,4477,4478,4481],{"class":3419,"line":4104},[3417,4479,4480],{"class":3734},"    print",[3417,4482,4483],{"class":3430},"(wallet.__funds)\n",[3417,4485,4486,4489,4492,4495],{"class":3419,"line":4109},[3417,4487,4488],{"class":3960},"except",[3417,4490,4491],{"class":3722}," AttributeError",[3417,4493,4494],{"class":3960}," as",[3417,4496,4497],{"class":3430}," e:\n",[3417,4499,4500,4502,4504,4506,4509,4511,4514,4516,4518],{"class":3419,"line":4127},[3417,4501,4480],{"class":3734},[3417,4503,3738],{"class":3430},[3417,4505,4001],{"class":3718},[3417,4507,4508],{"class":3434},"\"AttributeError: ",[3417,4510,4007],{"class":3718},[3417,4512,4513],{"class":3430},"e",[3417,4515,4012],{"class":3718},[3417,4517,4015],{"class":3434},[3417,4519,3445],{"class":3430},[3417,4521,4523],{"class":3419,"line":4522},23,[3417,4524,3452],{"emptyLinePlaceholder":3451},[3417,4526,4528],{"class":3419,"line":4527},24,[3417,4529,4530],{"class":3423},"# Досліджуємо __dict__ — бачимо викривлене ім'я\n",[3417,4532,4534,4536,4539,4542],{"class":3419,"line":4533},25,[3417,4535,4170],{"class":3734},[3417,4537,4538],{"class":3430},"(wallet.",[3417,4540,4541],{"class":3741},"__dict__",[3417,4543,3445],{"class":3430},[3417,4545,4547],{"class":3419,"line":4546},26,[3417,4548,3452],{"emptyLinePlaceholder":3451},[3417,4550,4552],{"class":3419,"line":4551},27,[3417,4553,4554],{"class":3423},"# Доступ через викривлене ім'я — все ще можливий!\n",[3417,4556,4558,4560],{"class":3419,"line":4557},28,[3417,4559,4170],{"class":3734},[3417,4561,4562],{"class":3430},"(wallet._SecureWallet__funds)\n",[3417,4564,4566,4569],{"class":3419,"line":4565},29,[3417,4567,4568],{"class":3430},"wallet._SecureWallet__funds = -",[3417,4570,4571],{"class":3441},"1000.0\n",[3417,4573,4575,4577],{"class":3419,"line":4574},30,[3417,4576,4170],{"class":3734},[3417,4578,4579],{"class":3430},"(wallet.get_funds())\n",[4189,4581,4583,4591,4600,4618,4630],{"title":4582},"python secure_wallet.py",[4193,4584,4586,4201,4589],{"className":4585},[3419],[3417,4587,4200],{"className":4588},[4199],[3516,4590,4582],{},[4193,4592,4594,4595],{"className":4593},[3419],"AttributeError: ",[3417,4596,4599],{"className":4597},[4598],"text-rose-400","'SecureWallet' object has no attribute '__funds'",[4193,4601,4603,4604,4609,4610,4614,4615,4012],{"className":4602},[3419],"{'owner': ",[3417,4605,4608],{"className":4606},[4607],"text-green-400","'Марія'",", '",[3417,4611,4613],{"className":4612},[4212],"_SecureWallet__funds","': ",[3417,4616,4457],{"className":4617},[4607],[4193,4619,4621,4625,4626],{"className":4620},[3419],[3417,4622,4457],{"className":4623},[4624],"text-blue-400","  ",[3417,4627,4629],{"className":4628},[4218],"# доступ через викривлене ім'я — все ще працює!",[4193,4631,4633,4625,4637],{"className":4632},[3419],[3417,4634,4636],{"className":4635},[4598],"-1000.0",[3417,4638,4640],{"className":4639},[4218],"# успішно перезаписано через mangled name",[4642,4643,4644,4647,4648,4651,4652,4655],"warning",{},[3516,4645,4646],{},"Name Mangling — це не засіб безпеки."," Він не шифрує дані і не захищає їх у пам'яті. ",[3399,4649,4650],{},"wallet._SecureWallet__funds"," є абсолютно легальним Python-кодом. Механізм призначений виключно для ",[3516,4653,4654],{},"запобігання випадковим колізіям імен"," у ієрархіях наслідування, а не для захисту від навмисного злому.",[3553,4657],{},[3560,4659,4661],{"id":4660},"справжнє-призначення-name-mangling-захист-від-колізій-при-наслідуванні","Справжнє призначення Name Mangling: захист від колізій при наслідуванні",[3395,4663,4664],{},"Ось реальна проблема, яку вирішує подвійне підкреслення. Без Name Mangling підклас міг би випадково перезаписати внутрішній атрибут батьківського класу:",[3408,4666,4668],{"className":3410,"code":4667,"language":3412,"meta":3413,"style":3413},"# collision_demo.py\n\nclass BaseConnector:\n    def __init__(self):\n        self.__timeout = 30  # → _BaseConnector__timeout\n\n    def connect(self):\n        print(f\"Базовий таймаут: {self.__timeout}с\")\n\n\nclass CustomConnector(BaseConnector):\n    def __init__(self):\n        super().__init__()\n        self.__timeout = 90  # → _CustomConnector__timeout (інше поле!)\n\n    def show_custom(self):\n        print(f\"Кастомний таймаут: {self.__timeout}с\")\n\n\nconn = CustomConnector()\nconn.connect()      # Базовий таймаут: 30с  ✅ НЕ перезаписано!\nconn.show_custom()  # Кастомний таймаут: 90с ✅\n\n# Два окремих поля у __dict__:\nprint(conn.__dict__)\n# {'_BaseConnector__timeout': 30, '_CustomConnector__timeout': 90}\n",[3399,4669,4670,4675,4679,4688,4700,4713,4717,4730,4755,4759,4763,4777,4789,4803,4815,4819,4832,4853,4857,4861,4866,4874,4882,4886,4891,4902],{"__ignoreMap":3413},[3417,4671,4672],{"class":3419,"line":3420},[3417,4673,4674],{"class":3423},"# collision_demo.py\n",[3417,4676,4677],{"class":3419,"line":3427},[3417,4678,3452],{"emptyLinePlaceholder":3451},[3417,4680,4681,4683,4686],{"class":3419,"line":3448},[3417,4682,3719],{"class":3718},[3417,4684,4685],{"class":3722}," BaseConnector",[3417,4687,3726],{"class":3430},[3417,4689,4690,4692,4694,4696,4698],{"class":3419,"line":3455},[3417,4691,3731],{"class":3718},[3417,4693,3735],{"class":3734},[3417,4695,3738],{"class":3430},[3417,4697,3742],{"class":3741},[3417,4699,3764],{"class":3430},[3417,4701,4702,4704,4707,4710],{"class":3419,"line":3461},[3417,4703,3769],{"class":3718},[3417,4705,4706],{"class":3430},".__timeout = ",[3417,4708,4709],{"class":3441},"30",[3417,4711,4712],{"class":3423},"  # → _BaseConnector__timeout\n",[3417,4714,4715],{"class":3419,"line":3473},[3417,4716,3452],{"emptyLinePlaceholder":3451},[3417,4718,4719,4721,4724,4726,4728],{"class":3419,"line":3485},[3417,4720,3731],{"class":3718},[3417,4722,4723],{"class":3734}," connect",[3417,4725,3738],{"class":3430},[3417,4727,3742],{"class":3741},[3417,4729,3764],{"class":3430},[3417,4731,4732,4735,4737,4739,4742,4745,4748,4750,4753],{"class":3419,"line":3490},[3417,4733,4734],{"class":3734},"        print",[3417,4736,3738],{"class":3430},[3417,4738,4001],{"class":3718},[3417,4740,4741],{"class":3434},"\"Базовий таймаут: ",[3417,4743,4744],{"class":3718},"{self",[3417,4746,4747],{"class":3430},".__timeout",[3417,4749,4012],{"class":3718},[3417,4751,4752],{"class":3434},"с\"",[3417,4754,3445],{"class":3430},[3417,4756,4757],{"class":3419,"line":3496},[3417,4758,3452],{"emptyLinePlaceholder":3451},[3417,4760,4761],{"class":3419,"line":3645},[3417,4762,3452],{"emptyLinePlaceholder":3451},[3417,4764,4765,4767,4770,4772,4775],{"class":3419,"line":3651},[3417,4766,3719],{"class":3718},[3417,4768,4769],{"class":3722}," CustomConnector",[3417,4771,3738],{"class":3430},[3417,4773,4774],{"class":3722},"BaseConnector",[3417,4776,3764],{"class":3430},[3417,4778,4779,4781,4783,4785,4787],{"class":3419,"line":3657},[3417,4780,3731],{"class":3718},[3417,4782,3735],{"class":3734},[3417,4784,3738],{"class":3430},[3417,4786,3742],{"class":3741},[3417,4788,3764],{"class":3430},[3417,4790,4791,4794,4797,4800],{"class":3419,"line":3662},[3417,4792,4793],{"class":3722},"        super",[3417,4795,4796],{"class":3430},"().",[3417,4798,4799],{"class":3734},"__init__",[3417,4801,4802],{"class":3430},"()\n",[3417,4804,4805,4807,4809,4812],{"class":3419,"line":3668},[3417,4806,3769],{"class":3718},[3417,4808,4706],{"class":3430},[3417,4810,4811],{"class":3441},"90",[3417,4813,4814],{"class":3423},"  # → _CustomConnector__timeout (інше поле!)\n",[3417,4816,4817],{"class":3419,"line":3673},[3417,4818,3452],{"emptyLinePlaceholder":3451},[3417,4820,4821,4823,4826,4828,4830],{"class":3419,"line":3679},[3417,4822,3731],{"class":3718},[3417,4824,4825],{"class":3734}," show_custom",[3417,4827,3738],{"class":3430},[3417,4829,3742],{"class":3741},[3417,4831,3764],{"class":3430},[3417,4833,4834,4836,4838,4840,4843,4845,4847,4849,4851],{"class":3419,"line":3685},[3417,4835,4734],{"class":3734},[3417,4837,3738],{"class":3430},[3417,4839,4001],{"class":3718},[3417,4841,4842],{"class":3434},"\"Кастомний таймаут: ",[3417,4844,4744],{"class":3718},[3417,4846,4747],{"class":3430},[3417,4848,4012],{"class":3718},[3417,4850,4752],{"class":3434},[3417,4852,3445],{"class":3430},[3417,4854,4855],{"class":3419,"line":4074},[3417,4856,3452],{"emptyLinePlaceholder":3451},[3417,4858,4859],{"class":3419,"line":4082},[3417,4860,3452],{"emptyLinePlaceholder":3451},[3417,4862,4863],{"class":3419,"line":4104},[3417,4864,4865],{"class":3430},"conn = CustomConnector()\n",[3417,4867,4868,4871],{"class":3419,"line":4109},[3417,4869,4870],{"class":3430},"conn.connect()      ",[3417,4872,4873],{"class":3423},"# Базовий таймаут: 30с  ✅ НЕ перезаписано!\n",[3417,4875,4876,4879],{"class":3419,"line":4127},[3417,4877,4878],{"class":3430},"conn.show_custom()  ",[3417,4880,4881],{"class":3423},"# Кастомний таймаут: 90с ✅\n",[3417,4883,4884],{"class":3419,"line":4522},[3417,4885,3452],{"emptyLinePlaceholder":3451},[3417,4887,4888],{"class":3419,"line":4527},[3417,4889,4890],{"class":3423},"# Два окремих поля у __dict__:\n",[3417,4892,4893,4895,4898,4900],{"class":3419,"line":4533},[3417,4894,4170],{"class":3734},[3417,4896,4897],{"class":3430},"(conn.",[3417,4899,4541],{"class":3741},[3417,4901,3445],{"class":3430},[3417,4903,4904],{"class":3419,"line":4546},[3417,4905,4906],{"class":3423},"# {'_BaseConnector__timeout': 30, '_CustomConnector__timeout': 90}\n",[4189,4908,4910,4918,4930,4938],{"title":4909},"python collision_demo.py",[4193,4911,4913,4201,4916],{"className":4912},[3419],[3417,4914,4200],{"className":4915},[4199],[3516,4917,4909],{},[4193,4919,4921,4922,4925,4926],{"className":4920},[3419],"Базовий таймаут: ",[3417,4923,4709],{"className":4924},[4607],"с  ",[3417,4927,4929],{"className":4928},[4218],"# НЕ перезаписано підкласом!",[4193,4931,4933,4934,4937],{"className":4932},[3419],"Кастомний таймаут: ",[3417,4935,4811],{"className":4936},[4624],"с",[4193,4939,4007,4941,4945,4946,4950],{"className":4940},[3419],[3417,4942,4944],{"className":4943},[4212],"'_BaseConnector__timeout'",": 30, ",[3417,4947,4949],{"className":4948},[4212],"'_CustomConnector__timeout'",": 90}",[4952,4953,4954,4955,4957,4958,4961,4962,4964],"tip",{},"Використовуйте ",[3399,4956,4267],{}," (подвійне підкреслення) ",[3516,4959,4960],{},"тільки"," коли проектуєте базовий клас бібліотеки і хочете захистити критичні внутрішні атрибути від випадкового перезапису у підкласах. Для всіх інших ситуацій — ",[3399,4963,3821],{}," (одне підкреслення) є достатнім і кращим вибором.",[3553,4966],{},[3560,4968,4970],{"id":4969},"порівняльна-таблиця-рівнів-доступу","Порівняльна таблиця рівнів доступу",[4972,4973,4974,4996],"table",{},[4975,4976,4977],"thead",{},[4978,4979,4980,4984,4987,4990,4993],"tr",{},[4981,4982,4983],"th",{},"Конвенція",[4981,4985,4986],{},"Приклад",[4981,4988,4989],{},"Значення",[4981,4991,4992],{},"Доступ ззовні",[4981,4994,4995],{},"Name Mangling",[4997,4998,4999,5019,5037,5059],"tbody",{},[4978,5000,5001,5005,5010,5013,5016],{},[5002,5003,5004],"td",{},"Без підкреслення",[5002,5006,5007],{},[3399,5008,5009],{},"self.name",[5002,5011,5012],{},"Публічний API",[5002,5014,5015],{},"✅ Заохочується",[5002,5017,5018],{},"❌",[4978,5020,5021,5024,5029,5032,5035],{},[5002,5022,5023],{},"Одне підкреслення",[5002,5025,5026],{},[3399,5027,5028],{},"self._balance",[5002,5030,5031],{},"Деталь реалізації",[5002,5033,5034],{},"⚠️ Угода (не заборонено)",[5002,5036,5018],{},[4978,5038,5039,5042,5047,5050,5056],{},[5002,5040,5041],{},"Два підкреслення",[5002,5043,5044],{},[3399,5045,5046],{},"self.__secret",[5002,5048,5049],{},"Захист від колізій",[5002,5051,5052,5053],{},"🔒 Лише через ",[3399,5054,5055],{},"_Class__secret",[5002,5057,5058],{},"✅",[4978,5060,5061,5064,5069,5072,5075],{},[5002,5062,5063],{},"Dunder",[5002,5065,5066],{},[3399,5067,5068],{},"self.__init__",[5002,5070,5071],{},"Магічний метод",[5002,5073,5074],{},"✅ Частина протоколу",[5002,5076,5077],{},"❌ (виняток)",[3553,5079],{},[3390,5081,5083,5084,5087],{"id":5082},"частина-iii-property-pythonic-шлях-до-валідації","Частина III: ",[3399,5085,5086],{},"@property"," — Pythonic-шлях до валідації",[3560,5089,5091],{"id":5090},"проблема-java-style-геттерівсеттерів","Проблема Java-style геттерів\u002Fсеттерів",[3395,5093,5094,5095,5098,5099,5102],{},"Розробники, що прийшли з Java або C#, часто несуть звичку обгортати кожне поле парою ",[3399,5096,5097],{},"getX()"," \u002F ",[3399,5100,5101],{},"setX()",":",[3408,5104,5106],{"className":3410,"code":5105,"language":3412,"meta":3413,"style":3413},"# ❌ Антипатерн: Java-style у Python\nclass UnpythonicAccount:\n    def __init__(self, balance: float):\n        self._balance = balance\n\n    def get_balance(self) -> float:       # зайвий шум\n        return self._balance\n\n    def set_balance(self, value: float):  # зайвий шум\n        if value \u003C 0:\n            raise ValueError(\"Баланс не може бути від'ємним\")\n        self._balance = value\n\naccount = UnpythonicAccount(1000.0)\naccount.set_balance(account.get_balance() + 500)  # некрасиво!\n",[3399,5107,5108,5113,5122,5142,5149,5153,5173,5181,5185,5210,5221,5234,5241,5245,5254],{"__ignoreMap":3413},[3417,5109,5110],{"class":3419,"line":3420},[3417,5111,5112],{"class":3423},"# ❌ Антипатерн: Java-style у Python\n",[3417,5114,5115,5117,5120],{"class":3419,"line":3427},[3417,5116,3719],{"class":3718},[3417,5118,5119],{"class":3722}," UnpythonicAccount",[3417,5121,3726],{"class":3430},[3417,5123,5124,5126,5128,5130,5132,5134,5136,5138,5140],{"class":3419,"line":3448},[3417,5125,3731],{"class":3718},[3417,5127,3735],{"class":3734},[3417,5129,3738],{"class":3430},[3417,5131,3742],{"class":3741},[3417,5133,3438],{"class":3430},[3417,5135,3405],{"class":3741},[3417,5137,3750],{"class":3430},[3417,5139,3536],{"class":3722},[3417,5141,3764],{"class":3430},[3417,5143,5144,5146],{"class":3419,"line":3455},[3417,5145,3769],{"class":3718},[3417,5147,5148],{"class":3430},"._balance = balance\n",[3417,5150,5151],{"class":3419,"line":3461},[3417,5152,3452],{"emptyLinePlaceholder":3451},[3417,5154,5155,5157,5159,5161,5163,5165,5167,5170],{"class":3419,"line":3473},[3417,5156,3731],{"class":3718},[3417,5158,4114],{"class":3734},[3417,5160,3738],{"class":3430},[3417,5162,3742],{"class":3741},[3417,5164,3950],{"class":3430},[3417,5166,3536],{"class":3722},[3417,5168,5169],{"class":3430},":       ",[3417,5171,5172],{"class":3423},"# зайвий шум\n",[3417,5174,5175,5177,5179],{"class":3419,"line":3485},[3417,5176,4130],{"class":3960},[3417,5178,4133],{"class":3718},[3417,5180,4136],{"class":3430},[3417,5182,5183],{"class":3419,"line":3490},[3417,5184,3452],{"emptyLinePlaceholder":3451},[3417,5186,5187,5189,5192,5194,5196,5198,5201,5203,5205,5208],{"class":3419,"line":3496},[3417,5188,3731],{"class":3718},[3417,5190,5191],{"class":3734}," set_balance",[3417,5193,3738],{"class":3430},[3417,5195,3742],{"class":3741},[3417,5197,3438],{"class":3430},[3417,5199,5200],{"class":3741},"value",[3417,5202,3750],{"class":3430},[3417,5204,3536],{"class":3722},[3417,5206,5207],{"class":3430},"):  ",[3417,5209,5172],{"class":3423},[3417,5211,5212,5214,5217,5219],{"class":3419,"line":3645},[3417,5213,3961],{"class":3960},[3417,5215,5216],{"class":3430}," value \u003C ",[3417,5218,3967],{"class":3441},[3417,5220,3726],{"class":3430},[3417,5222,5223,5225,5227,5229,5232],{"class":3419,"line":3651},[3417,5224,3974],{"class":3960},[3417,5226,3977],{"class":3722},[3417,5228,3738],{"class":3430},[3417,5230,5231],{"class":3434},"\"Баланс не може бути від'ємним\"",[3417,5233,3445],{"class":3430},[3417,5235,5236,5238],{"class":3419,"line":3657},[3417,5237,3769],{"class":3718},[3417,5239,5240],{"class":3430},"._balance = value\n",[3417,5242,5243],{"class":3419,"line":3662},[3417,5244,3452],{"emptyLinePlaceholder":3451},[3417,5246,5247,5250,5252],{"class":3419,"line":3668},[3417,5248,5249],{"class":3430},"account = UnpythonicAccount(",[3417,5251,4154],{"class":3441},[3417,5253,3445],{"class":3430},[3417,5255,5256,5259,5262,5265],{"class":3419,"line":3673},[3417,5257,5258],{"class":3430},"account.set_balance(account.get_balance() + ",[3417,5260,5261],{"class":3441},"500",[3417,5263,5264],{"class":3430},")  ",[3417,5266,5267],{"class":3423},"# некрасиво!\n",[3395,5269,5270,5273,5274,5276,5277,5102],{},[3516,5271,5272],{},"Pythonic-підхід:"," починайте з простого публічного атрибута. Якщо пізніше знадобиться валідація — перетворіть його на ",[3399,5275,5086],{},". Зовнішній код при цьому ",[3516,5278,5279],{},"не потребує змін",[3408,5281,5283],{"className":3410,"code":5282,"language":3412,"meta":3413,"style":3413},"# ✅ Правильно: починаємо просто\nclass PythonicAccount:\n    def __init__(self, balance: float):\n        self.balance = balance  # просто публічний атрибут\n\n# Якщо згодом потрібна валідація — перетворюємо на @property.\n# account.balance = 1500 — цей зовнішній код НЕ ЗМІНИТЬСЯ!\n",[3399,5284,5285,5290,5299,5319,5329,5333,5338],{"__ignoreMap":3413},[3417,5286,5287],{"class":3419,"line":3420},[3417,5288,5289],{"class":3423},"# ✅ Правильно: починаємо просто\n",[3417,5291,5292,5294,5297],{"class":3419,"line":3427},[3417,5293,3719],{"class":3718},[3417,5295,5296],{"class":3722}," PythonicAccount",[3417,5298,3726],{"class":3430},[3417,5300,5301,5303,5305,5307,5309,5311,5313,5315,5317],{"class":3419,"line":3448},[3417,5302,3731],{"class":3718},[3417,5304,3735],{"class":3734},[3417,5306,3738],{"class":3430},[3417,5308,3742],{"class":3741},[3417,5310,3438],{"class":3430},[3417,5312,3405],{"class":3741},[3417,5314,3750],{"class":3430},[3417,5316,3536],{"class":3722},[3417,5318,3764],{"class":3430},[3417,5320,5321,5323,5326],{"class":3419,"line":3455},[3417,5322,3769],{"class":3718},[3417,5324,5325],{"class":3430},".balance = balance  ",[3417,5327,5328],{"class":3423},"# просто публічний атрибут\n",[3417,5330,5331],{"class":3419,"line":3461},[3417,5332,3452],{"emptyLinePlaceholder":3451},[3417,5334,5335],{"class":3419,"line":3473},[3417,5336,5337],{"class":3423},"# Якщо згодом потрібна валідація — перетворюємо на @property.\n",[3417,5339,5340],{"class":3419,"line":3485},[3417,5341,5342],{"class":3423},"# account.balance = 1500 — цей зовнішній код НЕ ЗМІНИТЬСЯ!\n",[3553,5344],{},[3560,5346,5348,5349,5351],{"id":5347},"декоратор-property-обчислювальні-властивості-read-only","Декоратор ",[3399,5350,5086],{},": обчислювальні властивості (read-only)",[3395,5353,5354,5356,5357,5360],{},[3399,5355,5086],{}," перетворює метод на атрибут — зчитування відбувається без дужок ",[3399,5358,5359],{},"()",". За замовчуванням властивість є лише для читання.",[3408,5362,5364],{"className":3410,"code":5363,"language":3412,"meta":3413,"style":3413},"# circle.py\nimport math\n\nclass Circle:\n    def __init__(self, radius: float):\n        if radius \u003C= 0:\n            raise ValueError(\"Радіус має бути додатним\")\n        self.radius = radius\n\n    @property\n    def area(self) -> float:\n        \"\"\"Площа кола — обчислюється з radius при кожному зверненні.\"\"\"\n        return math.pi * self.radius ** 2\n\n    @property\n    def circumference(self) -> float:\n        \"\"\"Довжина кола.\"\"\"\n        return 2 * math.pi * self.radius\n\n    @property\n    def diameter(self) -> float:\n        \"\"\"Діаметр — обчислюється з radius.\"\"\"\n        return self.radius * 2\n\n\nc = Circle(5.0)\n\n# Звертаємось як до атрибута — БЕЗ дужок!\nprint(f\"Площа:    {c.area:.4f}\")\nprint(f\"Периметр: {c.circumference:.4f}\")\nprint(f\"Діаметр:  {c.diameter:.4f}\")\n\n# Можна змінити radius — всі властивості автоматично оновляться\nc.radius = 10.0\nprint(f\"Нова площа: {c.area:.4f}\")\n\n# Спроба записати у read-only властивість:\ntry:\n    c.area = 999.0\nexcept AttributeError as e:\n    print(f\"AttributeError: {e}\")\n",[3399,5365,5366,5371,5379,5383,5392,5413,5424,5437,5444,5448,5456,5473,5478,5493,5497,5503,5520,5525,5540,5544,5550,5567,5572,5583,5587,5591,5601,5605,5610,5633,5655,5678,5683,5689,5698,5720,5725,5731,5738,5747,5758],{"__ignoreMap":3413},[3417,5367,5368],{"class":3419,"line":3420},[3417,5369,5370],{"class":3423},"# circle.py\n",[3417,5372,5373,5376],{"class":3419,"line":3427},[3417,5374,5375],{"class":3960},"import",[3417,5377,5378],{"class":3430}," math\n",[3417,5380,5381],{"class":3419,"line":3448},[3417,5382,3452],{"emptyLinePlaceholder":3451},[3417,5384,5385,5387,5390],{"class":3419,"line":3455},[3417,5386,3719],{"class":3718},[3417,5388,5389],{"class":3722}," Circle",[3417,5391,3726],{"class":3430},[3417,5393,5394,5396,5398,5400,5402,5404,5407,5409,5411],{"class":3419,"line":3461},[3417,5395,3731],{"class":3718},[3417,5397,3735],{"class":3734},[3417,5399,3738],{"class":3430},[3417,5401,3742],{"class":3741},[3417,5403,3438],{"class":3430},[3417,5405,5406],{"class":3741},"radius",[3417,5408,3750],{"class":3430},[3417,5410,3536],{"class":3722},[3417,5412,3764],{"class":3430},[3417,5414,5415,5417,5420,5422],{"class":3419,"line":3473},[3417,5416,3961],{"class":3960},[3417,5418,5419],{"class":3430}," radius \u003C= ",[3417,5421,3967],{"class":3441},[3417,5423,3726],{"class":3430},[3417,5425,5426,5428,5430,5432,5435],{"class":3419,"line":3485},[3417,5427,3974],{"class":3960},[3417,5429,3977],{"class":3722},[3417,5431,3738],{"class":3430},[3417,5433,5434],{"class":3434},"\"Радіус має бути додатним\"",[3417,5436,3445],{"class":3430},[3417,5438,5439,5441],{"class":3419,"line":3490},[3417,5440,3769],{"class":3718},[3417,5442,5443],{"class":3430},".radius = radius\n",[3417,5445,5446],{"class":3419,"line":3496},[3417,5447,3452],{"emptyLinePlaceholder":3451},[3417,5449,5450,5453],{"class":3419,"line":3645},[3417,5451,5452],{"class":3734},"    @",[3417,5454,5455],{"class":3722},"property\n",[3417,5457,5458,5460,5463,5465,5467,5469,5471],{"class":3419,"line":3651},[3417,5459,3731],{"class":3718},[3417,5461,5462],{"class":3734}," area",[3417,5464,3738],{"class":3430},[3417,5466,3742],{"class":3741},[3417,5468,3950],{"class":3430},[3417,5470,3536],{"class":3722},[3417,5472,3726],{"class":3430},[3417,5474,5475],{"class":3419,"line":3657},[3417,5476,5477],{"class":3434},"        \"\"\"Площа кола — обчислюється з radius при кожному зверненні.\"\"\"\n",[3417,5479,5480,5482,5485,5487,5490],{"class":3419,"line":3662},[3417,5481,4130],{"class":3960},[3417,5483,5484],{"class":3430}," math.pi * ",[3417,5486,3742],{"class":3718},[3417,5488,5489],{"class":3430},".radius ** ",[3417,5491,5492],{"class":3441},"2\n",[3417,5494,5495],{"class":3419,"line":3668},[3417,5496,3452],{"emptyLinePlaceholder":3451},[3417,5498,5499,5501],{"class":3419,"line":3673},[3417,5500,5452],{"class":3734},[3417,5502,5455],{"class":3722},[3417,5504,5505,5507,5510,5512,5514,5516,5518],{"class":3419,"line":3679},[3417,5506,3731],{"class":3718},[3417,5508,5509],{"class":3734}," circumference",[3417,5511,3738],{"class":3430},[3417,5513,3742],{"class":3741},[3417,5515,3950],{"class":3430},[3417,5517,3536],{"class":3722},[3417,5519,3726],{"class":3430},[3417,5521,5522],{"class":3419,"line":3685},[3417,5523,5524],{"class":3434},"        \"\"\"Довжина кола.\"\"\"\n",[3417,5526,5527,5529,5532,5535,5537],{"class":3419,"line":4074},[3417,5528,4130],{"class":3960},[3417,5530,5531],{"class":3441}," 2",[3417,5533,5534],{"class":3430}," * math.pi * ",[3417,5536,3742],{"class":3718},[3417,5538,5539],{"class":3430},".radius\n",[3417,5541,5542],{"class":3419,"line":4082},[3417,5543,3452],{"emptyLinePlaceholder":3451},[3417,5545,5546,5548],{"class":3419,"line":4104},[3417,5547,5452],{"class":3734},[3417,5549,5455],{"class":3722},[3417,5551,5552,5554,5557,5559,5561,5563,5565],{"class":3419,"line":4109},[3417,5553,3731],{"class":3718},[3417,5555,5556],{"class":3734}," diameter",[3417,5558,3738],{"class":3430},[3417,5560,3742],{"class":3741},[3417,5562,3950],{"class":3430},[3417,5564,3536],{"class":3722},[3417,5566,3726],{"class":3430},[3417,5568,5569],{"class":3419,"line":4127},[3417,5570,5571],{"class":3434},"        \"\"\"Діаметр — обчислюється з radius.\"\"\"\n",[3417,5573,5574,5576,5578,5581],{"class":3419,"line":4522},[3417,5575,4130],{"class":3960},[3417,5577,4133],{"class":3718},[3417,5579,5580],{"class":3430},".radius * ",[3417,5582,5492],{"class":3441},[3417,5584,5585],{"class":3419,"line":4527},[3417,5586,3452],{"emptyLinePlaceholder":3451},[3417,5588,5589],{"class":3419,"line":4533},[3417,5590,3452],{"emptyLinePlaceholder":3451},[3417,5592,5593,5596,5599],{"class":3419,"line":4546},[3417,5594,5595],{"class":3430},"c = Circle(",[3417,5597,5598],{"class":3441},"5.0",[3417,5600,3445],{"class":3430},[3417,5602,5603],{"class":3419,"line":4551},[3417,5604,3452],{"emptyLinePlaceholder":3451},[3417,5606,5607],{"class":3419,"line":4557},[3417,5608,5609],{"class":3423},"# Звертаємось як до атрибута — БЕЗ дужок!\n",[3417,5611,5612,5614,5616,5618,5621,5623,5626,5629,5631],{"class":3419,"line":4565},[3417,5613,4170],{"class":3734},[3417,5615,3738],{"class":3430},[3417,5617,4001],{"class":3718},[3417,5619,5620],{"class":3434},"\"Площа:    ",[3417,5622,4007],{"class":3718},[3417,5624,5625],{"class":3430},"c.area",[3417,5627,5628],{"class":3718},":.4f}",[3417,5630,4015],{"class":3434},[3417,5632,3445],{"class":3430},[3417,5634,5635,5637,5639,5641,5644,5646,5649,5651,5653],{"class":3419,"line":4574},[3417,5636,4170],{"class":3734},[3417,5638,3738],{"class":3430},[3417,5640,4001],{"class":3718},[3417,5642,5643],{"class":3434},"\"Периметр: ",[3417,5645,4007],{"class":3718},[3417,5647,5648],{"class":3430},"c.circumference",[3417,5650,5628],{"class":3718},[3417,5652,4015],{"class":3434},[3417,5654,3445],{"class":3430},[3417,5656,5658,5660,5662,5664,5667,5669,5672,5674,5676],{"class":3419,"line":5657},31,[3417,5659,4170],{"class":3734},[3417,5661,3738],{"class":3430},[3417,5663,4001],{"class":3718},[3417,5665,5666],{"class":3434},"\"Діаметр:  ",[3417,5668,4007],{"class":3718},[3417,5670,5671],{"class":3430},"c.diameter",[3417,5673,5628],{"class":3718},[3417,5675,4015],{"class":3434},[3417,5677,3445],{"class":3430},[3417,5679,5681],{"class":3419,"line":5680},32,[3417,5682,3452],{"emptyLinePlaceholder":3451},[3417,5684,5686],{"class":3419,"line":5685},33,[3417,5687,5688],{"class":3423},"# Можна змінити radius — всі властивості автоматично оновляться\n",[3417,5690,5692,5695],{"class":3419,"line":5691},34,[3417,5693,5694],{"class":3430},"c.radius = ",[3417,5696,5697],{"class":3441},"10.0\n",[3417,5699,5701,5703,5705,5707,5710,5712,5714,5716,5718],{"class":3419,"line":5700},35,[3417,5702,4170],{"class":3734},[3417,5704,3738],{"class":3430},[3417,5706,4001],{"class":3718},[3417,5708,5709],{"class":3434},"\"Нова площа: ",[3417,5711,4007],{"class":3718},[3417,5713,5625],{"class":3430},[3417,5715,5628],{"class":3718},[3417,5717,4015],{"class":3434},[3417,5719,3445],{"class":3430},[3417,5721,5723],{"class":3419,"line":5722},36,[3417,5724,3452],{"emptyLinePlaceholder":3451},[3417,5726,5728],{"class":3419,"line":5727},37,[3417,5729,5730],{"class":3423},"# Спроба записати у read-only властивість:\n",[3417,5732,5734,5736],{"class":3419,"line":5733},38,[3417,5735,4473],{"class":3960},[3417,5737,3726],{"class":3430},[3417,5739,5741,5744],{"class":3419,"line":5740},39,[3417,5742,5743],{"class":3430},"    c.area = ",[3417,5745,5746],{"class":3441},"999.0\n",[3417,5748,5750,5752,5754,5756],{"class":3419,"line":5749},40,[3417,5751,4488],{"class":3960},[3417,5753,4491],{"class":3722},[3417,5755,4494],{"class":3960},[3417,5757,4497],{"class":3430},[3417,5759,5761,5763,5765,5767,5769,5771,5773,5775,5777],{"class":3419,"line":5760},41,[3417,5762,4480],{"class":3734},[3417,5764,3738],{"class":3430},[3417,5766,4001],{"class":3718},[3417,5768,4508],{"class":3434},[3417,5770,4007],{"class":3718},[3417,5772,4513],{"class":3430},[3417,5774,4012],{"class":3718},[3417,5776,4015],{"class":3434},[3417,5778,3445],{"class":3430},[4189,5780,5782,5790,5798,5806,5814,5822],{"title":5781},"python circle.py",[4193,5783,5785,4201,5788],{"className":5784},[3419],[3417,5786,4200],{"className":5787},[4199],[3516,5789,5781],{},[4193,5791,5793,5794],{"className":5792},[3419],"Площа:    ",[3417,5795,5797],{"className":5796},[4607],"78.5398",[4193,5799,5801,5802],{"className":5800},[3419],"Периметр: ",[3417,5803,5805],{"className":5804},[4607],"31.4159",[4193,5807,5809,5810],{"className":5808},[3419],"Діаметр:  ",[3417,5811,5813],{"className":5812},[4607],"10.0000",[4193,5815,5817,5818],{"className":5816},[3419],"Нова площа: ",[3417,5819,5821],{"className":5820},[4624],"314.1593",[4193,5823,4594,5825],{"className":5824},[3419],[3417,5826,5828],{"className":5827},[4598],"property 'area' of 'Circle' object has no setter",[3553,5830],{},[3560,5832,5834],{"id":5833},"геттери-сеттери-та-делітери","Геттери, Сеттери та Делітери",[3395,5836,5837,5838,5841,5842,5845,5846,5849],{},"Для запису та видалення використовуються декоратори ",[3399,5839,5840],{},".setter"," та ",[3399,5843,5844],{},".deleter",". Ім'я методу ",[3516,5847,5848],{},"завжди збігається"," з іменем властивості.",[3408,5851,5853],{"className":3410,"code":5852,"language":3412,"meta":3413,"style":3413},"# temperature.py\n\nclass Temperature:\n    \"\"\"\n    Зберігає температуру в Цельсіях, але дозволяє\n    читати\u002Fписати через Фаренгейти з автоматичною конвертацією.\n    \"\"\"\n\n    def __init__(self, celsius: float):\n        self.celsius = celsius  # ← виклик сеттера (валідація вже тут!)\n\n    # ── Цельсій ───────────────────────────────────────────────────────────────\n\n    @property\n    def celsius(self) -> float:\n        \"\"\"Геттер: повертає температуру в Цельсіях.\"\"\"\n        return self._celsius\n\n    @celsius.setter\n    def celsius(self, value: float) -> None:\n        \"\"\"Сеттер: валідує діапазон перед збереженням.\"\"\"\n        if not isinstance(value, (int, float)):\n            raise TypeError(f\"Очікується число, отримано: {type(value).__name__}\")\n        if value \u003C -273.15:\n            raise ValueError(f\"Температура {value}°C нижча за абсолютний нуль (-273.15°C)!\")\n        self._celsius = float(value)\n\n    # ── Фаренгейт ─────────────────────────────────────────────────────────────\n\n    @property\n    def fahrenheit(self) -> float:\n        \"\"\"Геттер: конвертує Цельсій у Фаренгейт.\"\"\"\n        return (self._celsius * 9 \u002F 5) + 32\n\n    @fahrenheit.setter\n    def fahrenheit(self, value: float) -> None:\n        \"\"\"Сеттер: конвертує Фаренгейт у Цельсій і записує через celsius.setter.\"\"\"\n        self.celsius = (value - 32) * 5 \u002F 9  # ← делегуємо валідацію celsius.setter!\n\n    @fahrenheit.deleter\n    def fahrenheit(self) -> None:\n        \"\"\"Делітер: очищає внутрішній стан.\"\"\"\n        print(\"Видалення температурних даних...\")\n        del self._celsius\n\n    # ── Кельвін (read-only) ───────────────────────────────────────────────────\n\n    @property\n    def kelvin(self) -> float:\n        \"\"\"Кельвін — лише читання, конвертується з Цельсія.\"\"\"\n        return self._celsius + 273.15\n",[3399,5854,5855,5860,5864,5873,5878,5883,5888,5892,5896,5917,5927,5931,5936,5940,5946,5963,5968,5977,5981,5986,6010,6015,6038,6069,6081,6105,6117,6121,6126,6130,6136,6153,6158,6184,6188,6193,6217,6222,6244,6248,6253,6269,6275,6287,6297,6302,6308,6313,6320,6338,6344],{"__ignoreMap":3413},[3417,5856,5857],{"class":3419,"line":3420},[3417,5858,5859],{"class":3423},"# temperature.py\n",[3417,5861,5862],{"class":3419,"line":3427},[3417,5863,3452],{"emptyLinePlaceholder":3451},[3417,5865,5866,5868,5871],{"class":3419,"line":3448},[3417,5867,3719],{"class":3718},[3417,5869,5870],{"class":3722}," Temperature",[3417,5872,3726],{"class":3430},[3417,5874,5875],{"class":3419,"line":3455},[3417,5876,5877],{"class":3434},"    \"\"\"\n",[3417,5879,5880],{"class":3419,"line":3461},[3417,5881,5882],{"class":3434},"    Зберігає температуру в Цельсіях, але дозволяє\n",[3417,5884,5885],{"class":3419,"line":3473},[3417,5886,5887],{"class":3434},"    читати\u002Fписати через Фаренгейти з автоматичною конвертацією.\n",[3417,5889,5890],{"class":3419,"line":3485},[3417,5891,5877],{"class":3434},[3417,5893,5894],{"class":3419,"line":3490},[3417,5895,3452],{"emptyLinePlaceholder":3451},[3417,5897,5898,5900,5902,5904,5906,5908,5911,5913,5915],{"class":3419,"line":3496},[3417,5899,3731],{"class":3718},[3417,5901,3735],{"class":3734},[3417,5903,3738],{"class":3430},[3417,5905,3742],{"class":3741},[3417,5907,3438],{"class":3430},[3417,5909,5910],{"class":3741},"celsius",[3417,5912,3750],{"class":3430},[3417,5914,3536],{"class":3722},[3417,5916,3764],{"class":3430},[3417,5918,5919,5921,5924],{"class":3419,"line":3645},[3417,5920,3769],{"class":3718},[3417,5922,5923],{"class":3430},".celsius = celsius  ",[3417,5925,5926],{"class":3423},"# ← виклик сеттера (валідація вже тут!)\n",[3417,5928,5929],{"class":3419,"line":3651},[3417,5930,3452],{"emptyLinePlaceholder":3451},[3417,5932,5933],{"class":3419,"line":3657},[3417,5934,5935],{"class":3423},"    # ── Цельсій ───────────────────────────────────────────────────────────────\n",[3417,5937,5938],{"class":3419,"line":3662},[3417,5939,3452],{"emptyLinePlaceholder":3451},[3417,5941,5942,5944],{"class":3419,"line":3668},[3417,5943,5452],{"class":3734},[3417,5945,5455],{"class":3722},[3417,5947,5948,5950,5953,5955,5957,5959,5961],{"class":3419,"line":3673},[3417,5949,3731],{"class":3718},[3417,5951,5952],{"class":3734}," celsius",[3417,5954,3738],{"class":3430},[3417,5956,3742],{"class":3741},[3417,5958,3950],{"class":3430},[3417,5960,3536],{"class":3722},[3417,5962,3726],{"class":3430},[3417,5964,5965],{"class":3419,"line":3679},[3417,5966,5967],{"class":3434},"        \"\"\"Геттер: повертає температуру в Цельсіях.\"\"\"\n",[3417,5969,5970,5972,5974],{"class":3419,"line":3685},[3417,5971,4130],{"class":3960},[3417,5973,4133],{"class":3718},[3417,5975,5976],{"class":3430},"._celsius\n",[3417,5978,5979],{"class":3419,"line":4074},[3417,5980,3452],{"emptyLinePlaceholder":3451},[3417,5982,5983],{"class":3419,"line":4082},[3417,5984,5985],{"class":3734},"    @celsius.setter\n",[3417,5987,5988,5990,5992,5994,5996,5998,6000,6002,6004,6006,6008],{"class":3419,"line":4104},[3417,5989,3731],{"class":3718},[3417,5991,5952],{"class":3734},[3417,5993,3738],{"class":3430},[3417,5995,3742],{"class":3741},[3417,5997,3438],{"class":3430},[3417,5999,5200],{"class":3741},[3417,6001,3750],{"class":3430},[3417,6003,3536],{"class":3722},[3417,6005,3950],{"class":3430},[3417,6007,3953],{"class":3718},[3417,6009,3726],{"class":3430},[3417,6011,6012],{"class":3419,"line":4109},[3417,6013,6014],{"class":3434},"        \"\"\"Сеттер: валідує діапазон перед збереженням.\"\"\"\n",[3417,6016,6017,6019,6022,6025,6028,6031,6033,6035],{"class":3419,"line":4127},[3417,6018,3961],{"class":3960},[3417,6020,6021],{"class":3718}," not",[3417,6023,6024],{"class":3734}," isinstance",[3417,6026,6027],{"class":3430},"(value, (",[3417,6029,6030],{"class":3722},"int",[3417,6032,3438],{"class":3430},[3417,6034,3536],{"class":3722},[3417,6036,6037],{"class":3430},")):\n",[3417,6039,6040,6042,6045,6047,6049,6052,6054,6057,6060,6063,6065,6067],{"class":3419,"line":4522},[3417,6041,3974],{"class":3960},[3417,6043,6044],{"class":3722}," TypeError",[3417,6046,3738],{"class":3430},[3417,6048,4001],{"class":3718},[3417,6050,6051],{"class":3434},"\"Очікується число, отримано: ",[3417,6053,4007],{"class":3718},[3417,6055,6056],{"class":3722},"type",[3417,6058,6059],{"class":3430},"(value).",[3417,6061,6062],{"class":3741},"__name__",[3417,6064,4012],{"class":3718},[3417,6066,4015],{"class":3434},[3417,6068,3445],{"class":3430},[3417,6070,6071,6073,6076,6079],{"class":3419,"line":4527},[3417,6072,3961],{"class":3960},[3417,6074,6075],{"class":3430}," value \u003C -",[3417,6077,6078],{"class":3441},"273.15",[3417,6080,3726],{"class":3430},[3417,6082,6083,6085,6087,6089,6091,6094,6096,6098,6100,6103],{"class":3419,"line":4533},[3417,6084,3974],{"class":3960},[3417,6086,3977],{"class":3722},[3417,6088,3738],{"class":3430},[3417,6090,4001],{"class":3718},[3417,6092,6093],{"class":3434},"\"Температура ",[3417,6095,4007],{"class":3718},[3417,6097,5200],{"class":3430},[3417,6099,4012],{"class":3718},[3417,6101,6102],{"class":3434},"°C нижча за абсолютний нуль (-273.15°C)!\"",[3417,6104,3445],{"class":3430},[3417,6106,6107,6109,6112,6114],{"class":3419,"line":4546},[3417,6108,3769],{"class":3718},[3417,6110,6111],{"class":3430},"._celsius = ",[3417,6113,3536],{"class":3722},[3417,6115,6116],{"class":3430},"(value)\n",[3417,6118,6119],{"class":3419,"line":4551},[3417,6120,3452],{"emptyLinePlaceholder":3451},[3417,6122,6123],{"class":3419,"line":4557},[3417,6124,6125],{"class":3423},"    # ── Фаренгейт ─────────────────────────────────────────────────────────────\n",[3417,6127,6128],{"class":3419,"line":4565},[3417,6129,3452],{"emptyLinePlaceholder":3451},[3417,6131,6132,6134],{"class":3419,"line":4574},[3417,6133,5452],{"class":3734},[3417,6135,5455],{"class":3722},[3417,6137,6138,6140,6143,6145,6147,6149,6151],{"class":3419,"line":5657},[3417,6139,3731],{"class":3718},[3417,6141,6142],{"class":3734}," fahrenheit",[3417,6144,3738],{"class":3430},[3417,6146,3742],{"class":3741},[3417,6148,3950],{"class":3430},[3417,6150,3536],{"class":3722},[3417,6152,3726],{"class":3430},[3417,6154,6155],{"class":3419,"line":5680},[3417,6156,6157],{"class":3434},"        \"\"\"Геттер: конвертує Цельсій у Фаренгейт.\"\"\"\n",[3417,6159,6160,6162,6165,6167,6170,6173,6175,6178,6181],{"class":3419,"line":5685},[3417,6161,4130],{"class":3960},[3417,6163,6164],{"class":3430}," (",[3417,6166,3742],{"class":3718},[3417,6168,6169],{"class":3430},"._celsius * ",[3417,6171,6172],{"class":3441},"9",[3417,6174,5098],{"class":3430},[3417,6176,6177],{"class":3441},"5",[3417,6179,6180],{"class":3430},") + ",[3417,6182,6183],{"class":3441},"32\n",[3417,6185,6186],{"class":3419,"line":5691},[3417,6187,3452],{"emptyLinePlaceholder":3451},[3417,6189,6190],{"class":3419,"line":5700},[3417,6191,6192],{"class":3734},"    @fahrenheit.setter\n",[3417,6194,6195,6197,6199,6201,6203,6205,6207,6209,6211,6213,6215],{"class":3419,"line":5722},[3417,6196,3731],{"class":3718},[3417,6198,6142],{"class":3734},[3417,6200,3738],{"class":3430},[3417,6202,3742],{"class":3741},[3417,6204,3438],{"class":3430},[3417,6206,5200],{"class":3741},[3417,6208,3750],{"class":3430},[3417,6210,3536],{"class":3722},[3417,6212,3950],{"class":3430},[3417,6214,3953],{"class":3718},[3417,6216,3726],{"class":3430},[3417,6218,6219],{"class":3419,"line":5727},[3417,6220,6221],{"class":3434},"        \"\"\"Сеттер: конвертує Фаренгейт у Цельсій і записує через celsius.setter.\"\"\"\n",[3417,6223,6224,6226,6229,6232,6235,6237,6239,6241],{"class":3419,"line":5733},[3417,6225,3769],{"class":3718},[3417,6227,6228],{"class":3430},".celsius = (value - ",[3417,6230,6231],{"class":3441},"32",[3417,6233,6234],{"class":3430},") * ",[3417,6236,6177],{"class":3441},[3417,6238,5098],{"class":3430},[3417,6240,6172],{"class":3441},[3417,6242,6243],{"class":3423},"  # ← делегуємо валідацію celsius.setter!\n",[3417,6245,6246],{"class":3419,"line":5740},[3417,6247,3452],{"emptyLinePlaceholder":3451},[3417,6249,6250],{"class":3419,"line":5749},[3417,6251,6252],{"class":3734},"    @fahrenheit.deleter\n",[3417,6254,6255,6257,6259,6261,6263,6265,6267],{"class":3419,"line":5760},[3417,6256,3731],{"class":3718},[3417,6258,6142],{"class":3734},[3417,6260,3738],{"class":3430},[3417,6262,3742],{"class":3741},[3417,6264,3950],{"class":3430},[3417,6266,3953],{"class":3718},[3417,6268,3726],{"class":3430},[3417,6270,6272],{"class":3419,"line":6271},42,[3417,6273,6274],{"class":3434},"        \"\"\"Делітер: очищає внутрішній стан.\"\"\"\n",[3417,6276,6278,6280,6282,6285],{"class":3419,"line":6277},43,[3417,6279,4734],{"class":3734},[3417,6281,3738],{"class":3430},[3417,6283,6284],{"class":3434},"\"Видалення температурних даних...\"",[3417,6286,3445],{"class":3430},[3417,6288,6290,6293,6295],{"class":3419,"line":6289},44,[3417,6291,6292],{"class":3960},"        del",[3417,6294,4133],{"class":3718},[3417,6296,5976],{"class":3430},[3417,6298,6300],{"class":3419,"line":6299},45,[3417,6301,3452],{"emptyLinePlaceholder":3451},[3417,6303,6305],{"class":3419,"line":6304},46,[3417,6306,6307],{"class":3423},"    # ── Кельвін (read-only) ───────────────────────────────────────────────────\n",[3417,6309,6311],{"class":3419,"line":6310},47,[3417,6312,3452],{"emptyLinePlaceholder":3451},[3417,6314,6316,6318],{"class":3419,"line":6315},48,[3417,6317,5452],{"class":3734},[3417,6319,5455],{"class":3722},[3417,6321,6323,6325,6328,6330,6332,6334,6336],{"class":3419,"line":6322},49,[3417,6324,3731],{"class":3718},[3417,6326,6327],{"class":3734}," kelvin",[3417,6329,3738],{"class":3430},[3417,6331,3742],{"class":3741},[3417,6333,3950],{"class":3430},[3417,6335,3536],{"class":3722},[3417,6337,3726],{"class":3430},[3417,6339,6341],{"class":3419,"line":6340},50,[3417,6342,6343],{"class":3434},"        \"\"\"Кельвін — лише читання, конвертується з Цельсія.\"\"\"\n",[3417,6345,6347,6349,6351,6354],{"class":3419,"line":6346},51,[3417,6348,4130],{"class":3960},[3417,6350,4133],{"class":3718},[3417,6352,6353],{"class":3430},"._celsius + ",[3417,6355,6356],{"class":3441},"273.15\n",[3408,6358,6360],{"className":3410,"code":6359,"language":3412,"meta":3413,"style":3413},"# Демонстрація\nt = Temperature(25.0)\nprint(f\"Цельсій:    {t.celsius}°C\")\nprint(f\"Фаренгейт: {t.fahrenheit}°F\")\nprint(f\"Кельвін:   {t.kelvin}K\")\n\n# Зміна через Фаренгейт — автоматична конвертація\nt.fahrenheit = 32.0\nprint(f\"\\nПісля t.fahrenheit = 32.0:\")\nprint(f\"Цельсій:    {t.celsius}°C\")\n\n# Валідація — нижче абсолютного нуля\ntry:\n    t.celsius = -300.0\nexcept ValueError as e:\n    print(f\"\\nValueError: {e}\")\n\n# Валідація типу\ntry:\n    t.celsius = \"гаряче\"\nexcept TypeError as e:\n    print(f\"TypeError: {e}\")\n\n# Делітер\ndel t.fahrenheit\ntry:\n    _ = t.celsius\nexcept AttributeError as e:\n    print(f\"\\nAttributeError після del: {e}\")\n",[3399,6361,6362,6367,6377,6400,6423,6446,6450,6455,6463,6482,6502,6506,6511,6517,6525,6535,6560,6564,6569,6575,6583,6593,6614,6618,6623,6631,6637,6642,6652],{"__ignoreMap":3413},[3417,6363,6364],{"class":3419,"line":3420},[3417,6365,6366],{"class":3423},"# Демонстрація\n",[3417,6368,6369,6372,6375],{"class":3419,"line":3427},[3417,6370,6371],{"class":3430},"t = Temperature(",[3417,6373,6374],{"class":3441},"25.0",[3417,6376,3445],{"class":3430},[3417,6378,6379,6381,6383,6385,6388,6390,6393,6395,6398],{"class":3419,"line":3448},[3417,6380,4170],{"class":3734},[3417,6382,3738],{"class":3430},[3417,6384,4001],{"class":3718},[3417,6386,6387],{"class":3434},"\"Цельсій:    ",[3417,6389,4007],{"class":3718},[3417,6391,6392],{"class":3430},"t.celsius",[3417,6394,4012],{"class":3718},[3417,6396,6397],{"class":3434},"°C\"",[3417,6399,3445],{"class":3430},[3417,6401,6402,6404,6406,6408,6411,6413,6416,6418,6421],{"class":3419,"line":3455},[3417,6403,4170],{"class":3734},[3417,6405,3738],{"class":3430},[3417,6407,4001],{"class":3718},[3417,6409,6410],{"class":3434},"\"Фаренгейт: ",[3417,6412,4007],{"class":3718},[3417,6414,6415],{"class":3430},"t.fahrenheit",[3417,6417,4012],{"class":3718},[3417,6419,6420],{"class":3434},"°F\"",[3417,6422,3445],{"class":3430},[3417,6424,6425,6427,6429,6431,6434,6436,6439,6441,6444],{"class":3419,"line":3461},[3417,6426,4170],{"class":3734},[3417,6428,3738],{"class":3430},[3417,6430,4001],{"class":3718},[3417,6432,6433],{"class":3434},"\"Кельвін:   ",[3417,6435,4007],{"class":3718},[3417,6437,6438],{"class":3430},"t.kelvin",[3417,6440,4012],{"class":3718},[3417,6442,6443],{"class":3434},"K\"",[3417,6445,3445],{"class":3430},[3417,6447,6448],{"class":3419,"line":3473},[3417,6449,3452],{"emptyLinePlaceholder":3451},[3417,6451,6452],{"class":3419,"line":3485},[3417,6453,6454],{"class":3423},"# Зміна через Фаренгейт — автоматична конвертація\n",[3417,6456,6457,6460],{"class":3419,"line":3490},[3417,6458,6459],{"class":3430},"t.fahrenheit = ",[3417,6461,6462],{"class":3441},"32.0\n",[3417,6464,6465,6467,6469,6471,6473,6477,6480],{"class":3419,"line":3496},[3417,6466,4170],{"class":3734},[3417,6468,3738],{"class":3430},[3417,6470,4001],{"class":3718},[3417,6472,4015],{"class":3434},[3417,6474,6476],{"class":6475},"sjcCO","\\n",[3417,6478,6479],{"class":3434},"Після t.fahrenheit = 32.0:\"",[3417,6481,3445],{"class":3430},[3417,6483,6484,6486,6488,6490,6492,6494,6496,6498,6500],{"class":3419,"line":3645},[3417,6485,4170],{"class":3734},[3417,6487,3738],{"class":3430},[3417,6489,4001],{"class":3718},[3417,6491,6387],{"class":3434},[3417,6493,4007],{"class":3718},[3417,6495,6392],{"class":3430},[3417,6497,4012],{"class":3718},[3417,6499,6397],{"class":3434},[3417,6501,3445],{"class":3430},[3417,6503,6504],{"class":3419,"line":3651},[3417,6505,3452],{"emptyLinePlaceholder":3451},[3417,6507,6508],{"class":3419,"line":3657},[3417,6509,6510],{"class":3423},"# Валідація — нижче абсолютного нуля\n",[3417,6512,6513,6515],{"class":3419,"line":3662},[3417,6514,4473],{"class":3960},[3417,6516,3726],{"class":3430},[3417,6518,6519,6522],{"class":3419,"line":3668},[3417,6520,6521],{"class":3430},"    t.celsius = -",[3417,6523,6524],{"class":3441},"300.0\n",[3417,6526,6527,6529,6531,6533],{"class":3419,"line":3673},[3417,6528,4488],{"class":3960},[3417,6530,3977],{"class":3722},[3417,6532,4494],{"class":3960},[3417,6534,4497],{"class":3430},[3417,6536,6537,6539,6541,6543,6545,6547,6550,6552,6554,6556,6558],{"class":3419,"line":3679},[3417,6538,4480],{"class":3734},[3417,6540,3738],{"class":3430},[3417,6542,4001],{"class":3718},[3417,6544,4015],{"class":3434},[3417,6546,6476],{"class":6475},[3417,6548,6549],{"class":3434},"ValueError: ",[3417,6551,4007],{"class":3718},[3417,6553,4513],{"class":3430},[3417,6555,4012],{"class":3718},[3417,6557,4015],{"class":3434},[3417,6559,3445],{"class":3430},[3417,6561,6562],{"class":3419,"line":3685},[3417,6563,3452],{"emptyLinePlaceholder":3451},[3417,6565,6566],{"class":3419,"line":4074},[3417,6567,6568],{"class":3423},"# Валідація типу\n",[3417,6570,6571,6573],{"class":3419,"line":4082},[3417,6572,4473],{"class":3960},[3417,6574,3726],{"class":3430},[3417,6576,6577,6580],{"class":3419,"line":4104},[3417,6578,6579],{"class":3430},"    t.celsius = ",[3417,6581,6582],{"class":3434},"\"гаряче\"\n",[3417,6584,6585,6587,6589,6591],{"class":3419,"line":4109},[3417,6586,4488],{"class":3960},[3417,6588,6044],{"class":3722},[3417,6590,4494],{"class":3960},[3417,6592,4497],{"class":3430},[3417,6594,6595,6597,6599,6601,6604,6606,6608,6610,6612],{"class":3419,"line":4127},[3417,6596,4480],{"class":3734},[3417,6598,3738],{"class":3430},[3417,6600,4001],{"class":3718},[3417,6602,6603],{"class":3434},"\"TypeError: ",[3417,6605,4007],{"class":3718},[3417,6607,4513],{"class":3430},[3417,6609,4012],{"class":3718},[3417,6611,4015],{"class":3434},[3417,6613,3445],{"class":3430},[3417,6615,6616],{"class":3419,"line":4522},[3417,6617,3452],{"emptyLinePlaceholder":3451},[3417,6619,6620],{"class":3419,"line":4527},[3417,6621,6622],{"class":3423},"# Делітер\n",[3417,6624,6625,6628],{"class":3419,"line":4533},[3417,6626,6627],{"class":3960},"del",[3417,6629,6630],{"class":3430}," t.fahrenheit\n",[3417,6632,6633,6635],{"class":3419,"line":4546},[3417,6634,4473],{"class":3960},[3417,6636,3726],{"class":3430},[3417,6638,6639],{"class":3419,"line":4551},[3417,6640,6641],{"class":3430},"    _ = t.celsius\n",[3417,6643,6644,6646,6648,6650],{"class":3419,"line":4557},[3417,6645,4488],{"class":3960},[3417,6647,4491],{"class":3722},[3417,6649,4494],{"class":3960},[3417,6651,4497],{"class":3430},[3417,6653,6654,6656,6658,6660,6662,6664,6667,6669,6671,6673,6675],{"class":3419,"line":4565},[3417,6655,4480],{"class":3734},[3417,6657,3738],{"class":3430},[3417,6659,4001],{"class":3718},[3417,6661,4015],{"class":3434},[3417,6663,6476],{"class":6475},[3417,6665,6666],{"class":3434},"AttributeError після del: ",[3417,6668,4007],{"class":3718},[3417,6670,4513],{"class":3430},[3417,6672,4012],{"class":3718},[3417,6674,4015],{"class":3434},[3417,6676,3445],{"class":3430},[4189,6678,6680,6688,6696,6705,6714,6717,6721,6728,6731,6738,6746,6749,6753],{"title":6679},"python temperature.py",[4193,6681,6683,4201,6686],{"className":6682},[3419],[3417,6684,4200],{"className":6685},[4199],[3516,6687,6679],{},[4193,6689,6691,6692,6695],{"className":6690},[3419],"Цельсій:    ",[3417,6693,6374],{"className":6694},[4607],"°C",[4193,6697,6699,6700,6704],{"className":6698},[3419],"Фаренгейт: ",[3417,6701,6703],{"className":6702},[4607],"77.0","°F",[4193,6706,6708,6709,6713],{"className":6707},[3419],"Кельвін:   ",[3417,6710,6712],{"className":6711},[4607],"298.15","K",[4193,6715],{"className":6716},[3419],[4193,6718,6720],{"className":6719},[3419],"Після t.fahrenheit = 32.0:",[4193,6722,6691,6724,6695],{"className":6723},[3419],[3417,6725,6727],{"className":6726},[4624],"0.0",[4193,6729],{"className":6730},[3419],[4193,6732,6549,6734],{"className":6733},[3419],[3417,6735,6737],{"className":6736},[4598],"Температура -300.0°C нижча за абсолютний нуль (-273.15°C)!",[4193,6739,6741,6742],{"className":6740},[3419],"TypeError: ",[3417,6743,6745],{"className":6744},[4598],"Очікується число, отримано: str",[4193,6747],{"className":6748},[3419],[4193,6750,6752],{"className":6751},[3419],"Видалення температурних даних...",[4193,6754,6666,6756],{"className":6755},[3419],[3417,6757,6759],{"className":6758},[4598],"'Temperature' object has no attribute '_celsius'",[3553,6761],{},[3560,6763,6765],{"id":6764},"критична-пастка-нескінченна-рекурсія","Критична пастка: нескінченна рекурсія",[3395,6767,6768],{},"Найпоширеніша помилка при написанні властивостей — збіг імені атрибута та назви сеттера:",[3408,6770,6772],{"className":3410,"code":6771,"language":3412,"meta":3413,"style":3413},"# ❌ НІКОЛИ ТАК НЕ РОБІТЬ\nclass BadTemperature:\n    def __init__(self, celsius):\n        self.celsius = celsius   # викликає setter\n\n    @property\n    def celsius(self):\n        return self.celsius      # ← рекурсія! викликає сам себе\n\n    @celsius.setter\n    def celsius(self, value):\n        self.celsius = value     # ← рекурсія! викликає сам себе\n",[3399,6773,6774,6779,6788,6804,6814,6818,6824,6836,6848,6852,6856,6872],{"__ignoreMap":3413},[3417,6775,6776],{"class":3419,"line":3420},[3417,6777,6778],{"class":3423},"# ❌ НІКОЛИ ТАК НЕ РОБІТЬ\n",[3417,6780,6781,6783,6786],{"class":3419,"line":3427},[3417,6782,3719],{"class":3718},[3417,6784,6785],{"class":3722}," BadTemperature",[3417,6787,3726],{"class":3430},[3417,6789,6790,6792,6794,6796,6798,6800,6802],{"class":3419,"line":3448},[3417,6791,3731],{"class":3718},[3417,6793,3735],{"class":3734},[3417,6795,3738],{"class":3430},[3417,6797,3742],{"class":3741},[3417,6799,3438],{"class":3430},[3417,6801,5910],{"class":3741},[3417,6803,3764],{"class":3430},[3417,6805,6806,6808,6811],{"class":3419,"line":3455},[3417,6807,3769],{"class":3718},[3417,6809,6810],{"class":3430},".celsius = celsius   ",[3417,6812,6813],{"class":3423},"# викликає setter\n",[3417,6815,6816],{"class":3419,"line":3461},[3417,6817,3452],{"emptyLinePlaceholder":3451},[3417,6819,6820,6822],{"class":3419,"line":3473},[3417,6821,5452],{"class":3734},[3417,6823,5455],{"class":3722},[3417,6825,6826,6828,6830,6832,6834],{"class":3419,"line":3485},[3417,6827,3731],{"class":3718},[3417,6829,5952],{"class":3734},[3417,6831,3738],{"class":3430},[3417,6833,3742],{"class":3741},[3417,6835,3764],{"class":3430},[3417,6837,6838,6840,6842,6845],{"class":3419,"line":3490},[3417,6839,4130],{"class":3960},[3417,6841,4133],{"class":3718},[3417,6843,6844],{"class":3430},".celsius      ",[3417,6846,6847],{"class":3423},"# ← рекурсія! викликає сам себе\n",[3417,6849,6850],{"class":3419,"line":3496},[3417,6851,3452],{"emptyLinePlaceholder":3451},[3417,6853,6854],{"class":3419,"line":3645},[3417,6855,5985],{"class":3734},[3417,6857,6858,6860,6862,6864,6866,6868,6870],{"class":3419,"line":3651},[3417,6859,3731],{"class":3718},[3417,6861,5952],{"class":3734},[3417,6863,3738],{"class":3430},[3417,6865,3742],{"class":3741},[3417,6867,3438],{"class":3430},[3417,6869,5200],{"class":3741},[3417,6871,3764],{"class":3430},[3417,6873,6874,6876,6879],{"class":3419,"line":3657},[3417,6875,3769],{"class":3718},[3417,6877,6878],{"class":3430},".celsius = value     ",[3417,6880,6847],{"class":3423},[4189,6882,6884,6893,6900],{"title":6883},"Що станеться при запуску",[4193,6885,6887,4201,6890],{"className":6886},[3419],[3417,6888,4200],{"className":6889},[4199],[3516,6891,6892],{},"python bad_temperature.py",[4193,6894,6896],{"className":6895},[3419],[3417,6897,6899],{"className":6898},[4598],"RecursionError: maximum recursion depth exceeded while calling a Python object",[4193,6901,6903],{"className":6902},[3419],[3417,6904,6906],{"className":6905},[4218],"# Python вийде з ладу через ~1000 вкладених викликів",[3395,6908,6909,6912,6913,6916,6917,5102],{},[3516,6910,6911],{},"Правило:"," внутрішній атрибут для зберігання даних ",[3516,6914,6915],{},"завжди"," відрізняється від імені властивості префіксом ",[3399,6918,4258],{},[3408,6920,6922],{"className":3410,"code":6921,"language":3412,"meta":3413,"style":3413},"# ✅ Правильно: властивість celsius, сховище _celsius\n@property\ndef celsius(self) -> float:\n    return self._celsius        # читаємо з _celsius\n\n@celsius.setter\ndef celsius(self, value: float) -> None:\n    self._celsius = value       # пишемо у _celsius\n",[3399,6923,6924,6929,6936,6953,6966,6970,6975,6999],{"__ignoreMap":3413},[3417,6925,6926],{"class":3419,"line":3420},[3417,6927,6928],{"class":3423},"# ✅ Правильно: властивість celsius, сховище _celsius\n",[3417,6930,6931,6934],{"class":3419,"line":3427},[3417,6932,6933],{"class":3734},"@",[3417,6935,5455],{"class":3722},[3417,6937,6938,6941,6943,6945,6947,6949,6951],{"class":3419,"line":3448},[3417,6939,6940],{"class":3718},"def",[3417,6942,5952],{"class":3734},[3417,6944,3738],{"class":3430},[3417,6946,3742],{"class":3741},[3417,6948,3950],{"class":3430},[3417,6950,3536],{"class":3722},[3417,6952,3726],{"class":3430},[3417,6954,6955,6958,6960,6963],{"class":3419,"line":3455},[3417,6956,6957],{"class":3960},"    return",[3417,6959,4133],{"class":3718},[3417,6961,6962],{"class":3430},"._celsius        ",[3417,6964,6965],{"class":3423},"# читаємо з _celsius\n",[3417,6967,6968],{"class":3419,"line":3461},[3417,6969,3452],{"emptyLinePlaceholder":3451},[3417,6971,6972],{"class":3419,"line":3473},[3417,6973,6974],{"class":3734},"@celsius.setter\n",[3417,6976,6977,6979,6981,6983,6985,6987,6989,6991,6993,6995,6997],{"class":3419,"line":3485},[3417,6978,6940],{"class":3718},[3417,6980,5952],{"class":3734},[3417,6982,3738],{"class":3430},[3417,6984,3742],{"class":3741},[3417,6986,3438],{"class":3430},[3417,6988,5200],{"class":3741},[3417,6990,3750],{"class":3430},[3417,6992,3536],{"class":3722},[3417,6994,3950],{"class":3430},[3417,6996,3953],{"class":3718},[3417,6998,3726],{"class":3430},[3417,7000,7001,7004,7007],{"class":3419,"line":3490},[3417,7002,7003],{"class":3718},"    self",[3417,7005,7006],{"class":3430},"._celsius = value       ",[3417,7008,7009],{"class":3423},"# пишемо у _celsius\n",[3553,7011],{},[3560,7013,7015,7016],{"id":7014},"декоратори-vs-функція-property","Декоратори vs функція ",[3399,7017,7018],{},"property()",[7020,7021,7022,7167],"tabs",{},[7023,7024,7026],"tabs-item",{"label":7025},"Через декоратори (сучасний стиль)",[3408,7027,7029],{"className":3410,"code":7028,"language":3412,"meta":3413,"style":3413},"class ModernAccount:\n    def __init__(self, balance: float):\n        self._balance = balance\n\n    @property\n    def balance(self) -> float:\n        \"\"\"Поточний баланс.\"\"\"\n        return self._balance\n\n    @balance.setter\n    def balance(self, value: float) -> None:\n        if value \u003C 0:\n            raise ValueError(\"Баланс не може бути від'ємним\")\n        self._balance = value\n",[3399,7030,7031,7040,7060,7066,7070,7076,7093,7098,7106,7110,7115,7139,7149,7161],{"__ignoreMap":3413},[3417,7032,7033,7035,7038],{"class":3419,"line":3420},[3417,7034,3719],{"class":3718},[3417,7036,7037],{"class":3722}," ModernAccount",[3417,7039,3726],{"class":3430},[3417,7041,7042,7044,7046,7048,7050,7052,7054,7056,7058],{"class":3419,"line":3427},[3417,7043,3731],{"class":3718},[3417,7045,3735],{"class":3734},[3417,7047,3738],{"class":3430},[3417,7049,3742],{"class":3741},[3417,7051,3438],{"class":3430},[3417,7053,3405],{"class":3741},[3417,7055,3750],{"class":3430},[3417,7057,3536],{"class":3722},[3417,7059,3764],{"class":3430},[3417,7061,7062,7064],{"class":3419,"line":3448},[3417,7063,3769],{"class":3718},[3417,7065,5148],{"class":3430},[3417,7067,7068],{"class":3419,"line":3455},[3417,7069,3452],{"emptyLinePlaceholder":3451},[3417,7071,7072,7074],{"class":3419,"line":3461},[3417,7073,5452],{"class":3734},[3417,7075,5455],{"class":3722},[3417,7077,7078,7080,7083,7085,7087,7089,7091],{"class":3419,"line":3473},[3417,7079,3731],{"class":3718},[3417,7081,7082],{"class":3734}," balance",[3417,7084,3738],{"class":3430},[3417,7086,3742],{"class":3741},[3417,7088,3950],{"class":3430},[3417,7090,3536],{"class":3722},[3417,7092,3726],{"class":3430},[3417,7094,7095],{"class":3419,"line":3485},[3417,7096,7097],{"class":3434},"        \"\"\"Поточний баланс.\"\"\"\n",[3417,7099,7100,7102,7104],{"class":3419,"line":3490},[3417,7101,4130],{"class":3960},[3417,7103,4133],{"class":3718},[3417,7105,4136],{"class":3430},[3417,7107,7108],{"class":3419,"line":3496},[3417,7109,3452],{"emptyLinePlaceholder":3451},[3417,7111,7112],{"class":3419,"line":3645},[3417,7113,7114],{"class":3734},"    @balance.setter\n",[3417,7116,7117,7119,7121,7123,7125,7127,7129,7131,7133,7135,7137],{"class":3419,"line":3651},[3417,7118,3731],{"class":3718},[3417,7120,7082],{"class":3734},[3417,7122,3738],{"class":3430},[3417,7124,3742],{"class":3741},[3417,7126,3438],{"class":3430},[3417,7128,5200],{"class":3741},[3417,7130,3750],{"class":3430},[3417,7132,3536],{"class":3722},[3417,7134,3950],{"class":3430},[3417,7136,3953],{"class":3718},[3417,7138,3726],{"class":3430},[3417,7140,7141,7143,7145,7147],{"class":3419,"line":3657},[3417,7142,3961],{"class":3960},[3417,7144,5216],{"class":3430},[3417,7146,3967],{"class":3441},[3417,7148,3726],{"class":3430},[3417,7150,7151,7153,7155,7157,7159],{"class":3419,"line":3662},[3417,7152,3974],{"class":3960},[3417,7154,3977],{"class":3722},[3417,7156,3738],{"class":3430},[3417,7158,5231],{"class":3434},[3417,7160,3445],{"class":3430},[3417,7162,7163,7165],{"class":3419,"line":3668},[3417,7164,3769],{"class":3718},[3417,7166,5240],{"class":3430},[7023,7168,7170],{"label":7169},"Через функцію property() (класичний)",[3408,7171,7173],{"className":3410,"code":7172,"language":3412,"meta":3413,"style":3413},"class ClassicAccount:\n    def __init__(self, balance: float):\n        self._balance = balance\n\n    def _get_balance(self) -> float:\n        return self._balance\n\n    def _set_balance(self, value: float) -> None:\n        if value \u003C 0:\n            raise ValueError(\"Баланс не може бути від'ємним\")\n        self._balance = value\n\n    # Зайві методи залишаються у просторі імен класу!\n    balance = property(fget=_get_balance, fset=_set_balance,\n                       doc=\"Поточний баланс.\")\n",[3399,7174,7175,7184,7204,7210,7214,7231,7239,7243,7268,7278,7290,7296,7300,7305,7327],{"__ignoreMap":3413},[3417,7176,7177,7179,7182],{"class":3419,"line":3420},[3417,7178,3719],{"class":3718},[3417,7180,7181],{"class":3722}," ClassicAccount",[3417,7183,3726],{"class":3430},[3417,7185,7186,7188,7190,7192,7194,7196,7198,7200,7202],{"class":3419,"line":3427},[3417,7187,3731],{"class":3718},[3417,7189,3735],{"class":3734},[3417,7191,3738],{"class":3430},[3417,7193,3742],{"class":3741},[3417,7195,3438],{"class":3430},[3417,7197,3405],{"class":3741},[3417,7199,3750],{"class":3430},[3417,7201,3536],{"class":3722},[3417,7203,3764],{"class":3430},[3417,7205,7206,7208],{"class":3419,"line":3448},[3417,7207,3769],{"class":3718},[3417,7209,5148],{"class":3430},[3417,7211,7212],{"class":3419,"line":3455},[3417,7213,3452],{"emptyLinePlaceholder":3451},[3417,7215,7216,7218,7221,7223,7225,7227,7229],{"class":3419,"line":3461},[3417,7217,3731],{"class":3718},[3417,7219,7220],{"class":3734}," _get_balance",[3417,7222,3738],{"class":3430},[3417,7224,3742],{"class":3741},[3417,7226,3950],{"class":3430},[3417,7228,3536],{"class":3722},[3417,7230,3726],{"class":3430},[3417,7232,7233,7235,7237],{"class":3419,"line":3473},[3417,7234,4130],{"class":3960},[3417,7236,4133],{"class":3718},[3417,7238,4136],{"class":3430},[3417,7240,7241],{"class":3419,"line":3485},[3417,7242,3452],{"emptyLinePlaceholder":3451},[3417,7244,7245,7247,7250,7252,7254,7256,7258,7260,7262,7264,7266],{"class":3419,"line":3490},[3417,7246,3731],{"class":3718},[3417,7248,7249],{"class":3734}," _set_balance",[3417,7251,3738],{"class":3430},[3417,7253,3742],{"class":3741},[3417,7255,3438],{"class":3430},[3417,7257,5200],{"class":3741},[3417,7259,3750],{"class":3430},[3417,7261,3536],{"class":3722},[3417,7263,3950],{"class":3430},[3417,7265,3953],{"class":3718},[3417,7267,3726],{"class":3430},[3417,7269,7270,7272,7274,7276],{"class":3419,"line":3496},[3417,7271,3961],{"class":3960},[3417,7273,5216],{"class":3430},[3417,7275,3967],{"class":3441},[3417,7277,3726],{"class":3430},[3417,7279,7280,7282,7284,7286,7288],{"class":3419,"line":3645},[3417,7281,3974],{"class":3960},[3417,7283,3977],{"class":3722},[3417,7285,3738],{"class":3430},[3417,7287,5231],{"class":3434},[3417,7289,3445],{"class":3430},[3417,7291,7292,7294],{"class":3419,"line":3651},[3417,7293,3769],{"class":3718},[3417,7295,5240],{"class":3430},[3417,7297,7298],{"class":3419,"line":3657},[3417,7299,3452],{"emptyLinePlaceholder":3451},[3417,7301,7302],{"class":3419,"line":3662},[3417,7303,7304],{"class":3423},"    # Зайві методи залишаються у просторі імен класу!\n",[3417,7306,7307,7310,7313,7315,7318,7321,7324],{"class":3419,"line":3668},[3417,7308,7309],{"class":3430},"    balance = ",[3417,7311,7312],{"class":3722},"property",[3417,7314,3738],{"class":3430},[3417,7316,7317],{"class":3741},"fget",[3417,7319,7320],{"class":3430},"=_get_balance, ",[3417,7322,7323],{"class":3741},"fset",[3417,7325,7326],{"class":3430},"=_set_balance,\n",[3417,7328,7329,7332,7335,7338],{"class":3419,"line":3673},[3417,7330,7331],{"class":3741},"                       doc",[3417,7333,7334],{"class":3430},"=",[3417,7336,7337],{"class":3434},"\"Поточний баланс.\"",[3417,7339,3445],{"class":3430},[7341,7342,7343,7351,7357,7367],"field-group",{},[7344,7345,7347,7348,7350],"field",{"name":7317,"type":7346},"callable | None = None","Функція-геттер. Приймає лише ",[3399,7349,3742],{},", повертає значення властивості.",[7344,7352,7353,7354,7356],{"name":7323,"type":7346},"Функція-сеттер. Приймає ",[3399,7355,3742],{}," та нове значення. Якщо не вказано — властивість є read-only.",[7344,7358,7360,7361,7363,7364,3589],{"name":7359,"type":7346},"fdel","Функція-делітер. Приймає лише ",[3399,7362,3742],{},". Викликається при ",[3399,7365,7366],{},"del obj.prop",[7344,7368,7371,7372,3589],{"name":7369,"type":7370},"doc","str | None = None","Документаційний рядок. Якщо не вказано — береться з ",[3399,7373,7374],{},"fget.__doc__",[3395,7376,7377],{},[3516,7378,7379],{},"Чому декоратори кращі:",[7381,7382,7383,7393,7396],"ol",{},[7384,7385,7386,5841,7389,7392],"li",{},[3399,7387,7388],{},"_get_balance",[3399,7390,7391],{},"_set_balance"," не засмічують публічний інтерфейс класу",[7384,7394,7395],{},"Геттер і сеттер розташовані поряд — одразу зрозуміло, що вони пов'язані",[7384,7397,7398,7399,7401],{},"Документаційний рядок природно прикріплений до ",[3399,7400,5086],{},"-методу",[3553,7403],{},[3390,7405,7407],{"id":7406},"частина-iv-під-капотом-протокол-дескрипторів","Частина IV: Під капотом — Протокол Дескрипторів",[3560,7409,7411],{"id":7410},"що-таке-дескриптор","Що таке дескриптор",[3395,7413,7414,7416,7417,7420,7421,7423,7424,7427],{},[3399,7415,5086],{}," — це ",[3516,7418,7419],{},"не магія",". Це об'єкт класу ",[3399,7422,7312],{},", що реалізує ",[3516,7425,7426],{},"протокол дескрипторів"," — набір спеціальних методів для перехоплення доступу до атрибутів.",[7341,7429,7430,7449,7456,7465],{},[7344,7431,7434,7435,7438,7439,7442,7443,7445,7446,7448],{"name":7432,"type":7433},"__get__(self, instance, owner)","descriptor method","Викликається при ",[3516,7436,7437],{},"зчитуванні"," атрибута. ",[3399,7440,7441],{},"instance"," — об'єкт-власник (або ",[3399,7444,3953],{}," при зверненні через клас). ",[3399,7447,3871],{}," — клас-власник.",[7344,7450,7434,7452,7455],{"name":7451,"type":7433},"__set__(self, instance, value)",[3516,7453,7454],{},"записі"," значення в атрибут. Наявність цього методу робить дескриптор «data descriptor».",[7344,7457,7434,7459,7462,7463,3589],{"name":7458,"type":7433},"__delete__(self, instance)",[3516,7460,7461],{},"видаленні"," атрибута через ",[3399,7464,6627],{},[7344,7466,7469,7470,7473],{"name":7467,"type":7468},"__set_name__(self, owner, name)","descriptor method (Python 3.6+)","Викликається ",[3516,7471,7472],{},"один раз"," під час парсингу класу. Дозволяє дескриптору дізнатися ім'я атрибута, якому він призначений. Замінює необхідність вручну передавати ім'я у конструктор.",[3560,7475,7477],{"id":7476},"алгоритм-пошуку-атрибутів-attribute-lookup","Алгоритм пошуку атрибутів (Attribute Lookup)",[3395,7479,7480,7481,7484,7485,7491],{},"Коли Python виконує ",[3399,7482,7483],{},"obj.attr",", він ",[3516,7486,7487,7488],{},"не просто шукає у ",[3399,7489,7490],{},"obj.__dict__",". Алгоритм суворо визначений:",[7493,7494,7495,7499,7510,7514,7542,7548,7553,7557,7575,7582],"steps",{},[3560,7496,7498],{"id":7497},"крок-1-пошук-у-класі-та-mro","Крок 1: Пошук у класі та MRO",[3395,7500,7501,7502,7505,7506,7509],{},"Python шукає ",[3399,7503,7504],{},"attr"," у ",[3399,7507,7508],{},"type(obj).__dict__"," та класах по ланцюжку MRO.",[3560,7511,7513],{"id":7512},"крок-2-data-descriptor-має-найвищий-пріоритет","Крок 2: Data Descriptor має найвищий пріоритет",[3395,7515,7516,7517,4201,7520,4201,7523,7526,7527,7530,7531,7534,7535,7538,7539,7541],{},"Якщо знайдено атрибут, що реалізує ",[3399,7518,7519],{},"__get__",[3516,7521,7522],{},"та",[3399,7524,7525],{},"__set__"," (або ",[3399,7528,7529],{},"__delete__",") — це ",[3516,7532,7533],{},"data descriptor",". Викликається ",[3399,7536,7537],{},"descriptor.__get__(obj, type(obj))",". Значення в ",[3399,7540,7490],{}," повністю ігнорується.",[3560,7543,7545,7546],{"id":7544},"крок-3-пошук-у-obj__dict__","Крок 3: Пошук у ",[3399,7547,7490],{},[3395,7549,7550,7551,3589],{},"Якщо data descriptor не знайдено — шукаємо у власному словнику екземпляра ",[3399,7552,7490],{},[3560,7554,7556],{"id":7555},"крок-4-non-data-descriptor-або-атрибут-класу","Крок 4: Non-data Descriptor або атрибут класу",[3395,7558,7559,7560,7562,7563,7416,7565,7568,7569,7571,7572,7574],{},"Якщо знайдено атрибут з ",[3399,7561,7519],{}," але без ",[3399,7564,7525],{},[3516,7566,7567],{},"non-data descriptor"," (наприклад, звичайна функція). Викликається ",[3399,7570,7519],{},". Якщо ",[3399,7573,7519],{}," немає — повертається атрибут класу.",[3560,7576,7578,7579],{"id":7577},"крок-5-__getattr__","Крок 5: ",[3399,7580,7581],{},"__getattr__",[3395,7583,7584,7585,7587],{},"Якщо нічого не знайдено — викликається ",[3399,7586,7581],{},", якщо він визначений.",[3591,7589,7590],{},[3408,7591,7593],{"className":3595,"code":7592,"language":3597,"meta":3413,"style":3413},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam ArrowColor #6366f1\n\nstart\n\n:obj.attr;\n\n:Пошук attr у type(obj).__mro__;\n\nif (Знайдено data descriptor\\n(__get__ + __set__)?) then (Так)\n    :Викликати descriptor.__get__(obj, type)\\n**найвищий пріоритет**;\n    stop\nelse (Ні)\n    if (Є в obj.__dict__?) then (Так)\n        :Повернути obj.__dict__['attr'];\n        stop\n    else (Ні)\n        if (Знайдено non-data descriptor\\n(тільки __get__)?) then (Так)\n            :Викликати descriptor.__get__(obj, type);\n            stop\n        else (Ні)\n            if (__getattr__ визначено?) then (Так)\n                :Викликати __getattr__(obj, 'attr');\n                stop\n            else (Ні)\n                :raise AttributeError;\n                stop\n            endif\n        endif\n    endif\nendif\n@enduml\n",[3399,7594,7595,7599,7603,7607,7611,7615,7620,7624,7629,7633,7638,7642,7647,7652,7657,7662,7667,7672,7677,7682,7687,7692,7697,7702,7707,7712,7717,7722,7727,7731,7736,7741,7746,7751],{"__ignoreMap":3413},[3417,7596,7597],{"class":3419,"line":3420},[3417,7598,3604],{},[3417,7600,7601],{"class":3419,"line":3427},[3417,7602,3609],{},[3417,7604,7605],{"class":3419,"line":3448},[3417,7606,3614],{},[3417,7608,7609],{"class":3419,"line":3455},[3417,7610,3619],{},[3417,7612,7613],{"class":3419,"line":3461},[3417,7614,3452],{"emptyLinePlaceholder":3451},[3417,7616,7617],{"class":3419,"line":3473},[3417,7618,7619],{},"start\n",[3417,7621,7622],{"class":3419,"line":3485},[3417,7623,3452],{"emptyLinePlaceholder":3451},[3417,7625,7626],{"class":3419,"line":3490},[3417,7627,7628],{},":obj.attr;\n",[3417,7630,7631],{"class":3419,"line":3496},[3417,7632,3452],{"emptyLinePlaceholder":3451},[3417,7634,7635],{"class":3419,"line":3645},[3417,7636,7637],{},":Пошук attr у type(obj).__mro__;\n",[3417,7639,7640],{"class":3419,"line":3651},[3417,7641,3452],{"emptyLinePlaceholder":3451},[3417,7643,7644],{"class":3419,"line":3657},[3417,7645,7646],{},"if (Знайдено data descriptor\\n(__get__ + __set__)?) then (Так)\n",[3417,7648,7649],{"class":3419,"line":3662},[3417,7650,7651],{},"    :Викликати descriptor.__get__(obj, type)\\n**найвищий пріоритет**;\n",[3417,7653,7654],{"class":3419,"line":3668},[3417,7655,7656],{},"    stop\n",[3417,7658,7659],{"class":3419,"line":3673},[3417,7660,7661],{},"else (Ні)\n",[3417,7663,7664],{"class":3419,"line":3679},[3417,7665,7666],{},"    if (Є в obj.__dict__?) then (Так)\n",[3417,7668,7669],{"class":3419,"line":3685},[3417,7670,7671],{},"        :Повернути obj.__dict__['attr'];\n",[3417,7673,7674],{"class":3419,"line":4074},[3417,7675,7676],{},"        stop\n",[3417,7678,7679],{"class":3419,"line":4082},[3417,7680,7681],{},"    else (Ні)\n",[3417,7683,7684],{"class":3419,"line":4104},[3417,7685,7686],{},"        if (Знайдено non-data descriptor\\n(тільки __get__)?) then (Так)\n",[3417,7688,7689],{"class":3419,"line":4109},[3417,7690,7691],{},"            :Викликати descriptor.__get__(obj, type);\n",[3417,7693,7694],{"class":3419,"line":4127},[3417,7695,7696],{},"            stop\n",[3417,7698,7699],{"class":3419,"line":4522},[3417,7700,7701],{},"        else (Ні)\n",[3417,7703,7704],{"class":3419,"line":4527},[3417,7705,7706],{},"            if (__getattr__ визначено?) then (Так)\n",[3417,7708,7709],{"class":3419,"line":4533},[3417,7710,7711],{},"                :Викликати __getattr__(obj, 'attr');\n",[3417,7713,7714],{"class":3419,"line":4546},[3417,7715,7716],{},"                stop\n",[3417,7718,7719],{"class":3419,"line":4551},[3417,7720,7721],{},"            else (Ні)\n",[3417,7723,7724],{"class":3419,"line":4557},[3417,7725,7726],{},"                :raise AttributeError;\n",[3417,7728,7729],{"class":3419,"line":4565},[3417,7730,7716],{},[3417,7732,7733],{"class":3419,"line":4574},[3417,7734,7735],{},"            endif\n",[3417,7737,7738],{"class":3419,"line":5657},[3417,7739,7740],{},"        endif\n",[3417,7742,7743],{"class":3419,"line":5680},[3417,7744,7745],{},"    endif\n",[3417,7747,7748],{"class":3419,"line":5685},[3417,7749,7750],{},"endif\n",[3417,7752,7753],{"class":3419,"line":5691},[3417,7754,3688],{},[3395,7756,7757,7758,7761,7762,7764,7765,7768,7769,7771,7772,7774,7775,7778,7779,3589],{},"Саме тому ",[3399,7759,7760],{},"obj.area = 100"," на read-only ",[3399,7763,5086],{}," кидає ",[3399,7766,7767],{},"AttributeError"," — ",[3399,7770,7312],{}," є data descriptor (має ",[3399,7773,7525],{},", що кидає помилку без сеттера), і він перехоплює запис ",[3516,7776,7777],{},"до"," того, як Python дивиться у ",[3399,7780,7490],{},[3560,7782,7784],{"id":7783},"sequence-diagram-читання-та-запис-через-дескриптор","Sequence Diagram: читання та запис через дескриптор",[3591,7786,7787],{},[3408,7788,7790],{"className":3595,"code":7789,"language":3597,"meta":3413,"style":3413},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam ArrowColor #6366f1\nskinparam sequence {\n    ParticipantBackgroundColor #f3f4f6\n    ParticipantBorderColor #d1d5db\n}\n\nactor \"Клієнтський код\" as Client\ncontrol \"Python Runtime\\n(Attribute Lookup)\" as Runtime\nparticipant \"celsius : property\\n(Descriptor)\" as Descriptor\nentity \"instance : Temperature\" as Instance\n\n== Зчитування: instance.celsius ==\nClient -> Runtime : instance.celsius\nRuntime -> Descriptor : data descriptor знайдено!\\n__get__(instance, Temperature)\nDescriptor -> Instance : fget(instance) → читає _celsius\nInstance --> Descriptor : 25.0\nDescriptor --> Runtime : 25.0\nRuntime --> Client : 25.0\n\n== Запис: instance.celsius = -300 ==\nClient -> Runtime : instance.celsius = -300\nRuntime -> Descriptor : __set__(instance, -300)\nDescriptor -> Descriptor : Валідація: -300 \u003C -273.15 → True\nDescriptor --> Runtime : raise ValueError\nRuntime --> Client : ValueError!\n@enduml\n",[3399,7791,7792,7796,7800,7804,7808,7813,7818,7823,7827,7831,7836,7841,7846,7851,7855,7860,7865,7870,7875,7880,7885,7890,7894,7899,7904,7909,7914,7919,7924],{"__ignoreMap":3413},[3417,7793,7794],{"class":3419,"line":3420},[3417,7795,3604],{},[3417,7797,7798],{"class":3419,"line":3427},[3417,7799,3609],{},[3417,7801,7802],{"class":3419,"line":3448},[3417,7803,3614],{},[3417,7805,7806],{"class":3419,"line":3455},[3417,7807,3619],{},[3417,7809,7810],{"class":3419,"line":3461},[3417,7811,7812],{},"skinparam sequence {\n",[3417,7814,7815],{"class":3419,"line":3473},[3417,7816,7817],{},"    ParticipantBackgroundColor #f3f4f6\n",[3417,7819,7820],{"class":3419,"line":3485},[3417,7821,7822],{},"    ParticipantBorderColor #d1d5db\n",[3417,7824,7825],{"class":3419,"line":3490},[3417,7826,3654],{},[3417,7828,7829],{"class":3419,"line":3496},[3417,7830,3452],{"emptyLinePlaceholder":3451},[3417,7832,7833],{"class":3419,"line":3645},[3417,7834,7835],{},"actor \"Клієнтський код\" as Client\n",[3417,7837,7838],{"class":3419,"line":3651},[3417,7839,7840],{},"control \"Python Runtime\\n(Attribute Lookup)\" as Runtime\n",[3417,7842,7843],{"class":3419,"line":3657},[3417,7844,7845],{},"participant \"celsius : property\\n(Descriptor)\" as Descriptor\n",[3417,7847,7848],{"class":3419,"line":3662},[3417,7849,7850],{},"entity \"instance : Temperature\" as Instance\n",[3417,7852,7853],{"class":3419,"line":3668},[3417,7854,3452],{"emptyLinePlaceholder":3451},[3417,7856,7857],{"class":3419,"line":3673},[3417,7858,7859],{},"== Зчитування: instance.celsius ==\n",[3417,7861,7862],{"class":3419,"line":3679},[3417,7863,7864],{},"Client -> Runtime : instance.celsius\n",[3417,7866,7867],{"class":3419,"line":3685},[3417,7868,7869],{},"Runtime -> Descriptor : data descriptor знайдено!\\n__get__(instance, Temperature)\n",[3417,7871,7872],{"class":3419,"line":4074},[3417,7873,7874],{},"Descriptor -> Instance : fget(instance) → читає _celsius\n",[3417,7876,7877],{"class":3419,"line":4082},[3417,7878,7879],{},"Instance --> Descriptor : 25.0\n",[3417,7881,7882],{"class":3419,"line":4104},[3417,7883,7884],{},"Descriptor --> Runtime : 25.0\n",[3417,7886,7887],{"class":3419,"line":4109},[3417,7888,7889],{},"Runtime --> Client : 25.0\n",[3417,7891,7892],{"class":3419,"line":4127},[3417,7893,3452],{"emptyLinePlaceholder":3451},[3417,7895,7896],{"class":3419,"line":4522},[3417,7897,7898],{},"== Запис: instance.celsius = -300 ==\n",[3417,7900,7901],{"class":3419,"line":4527},[3417,7902,7903],{},"Client -> Runtime : instance.celsius = -300\n",[3417,7905,7906],{"class":3419,"line":4533},[3417,7907,7908],{},"Runtime -> Descriptor : __set__(instance, -300)\n",[3417,7910,7911],{"class":3419,"line":4546},[3417,7912,7913],{},"Descriptor -> Descriptor : Валідація: -300 \u003C -273.15 → True\n",[3417,7915,7916],{"class":3419,"line":4551},[3417,7917,7918],{},"Descriptor --> Runtime : raise ValueError\n",[3417,7920,7921],{"class":3419,"line":4557},[3417,7922,7923],{},"Runtime --> Client : ValueError!\n",[3417,7925,7926],{"class":3419,"line":4565},[3417,7927,3688],{},[3560,7929,7931,7932,7934],{"id":7930},"симуляція-property-на-чистому-python","Симуляція ",[3399,7933,7312],{}," на чистому Python",[3395,7936,7937,7938,7940],{},"Щоб остаточно демістифікувати ",[3399,7939,5086],{},", напишемо власну реалізацію:",[3408,7942,7944],{"className":3410,"code":7943,"language":3412,"meta":3413,"style":3413},"class SimplifiedProperty:\n    \"\"\"\n    Спрощена реалізація вбудованого property на чистому Python.\n    Демонструє протокол дескрипторів у дії.\n    \"\"\"\n\n    def __init__(self, fget=None, fset=None, fdel=None, doc=None):\n        self.fget = fget\n        self.fset = fset\n        self.fdel = fdel\n        self.__doc__ = doc or (fget.__doc__ if fget else None)\n\n    def __get__(self, instance, owner):\n        # Звернення через клас (наприклад, Temperature.celsius)\n        if instance is None:\n            return self\n        if self.fget is None:\n            raise AttributeError(\"Не можна зчитати: геттер не визначено\")\n        return self.fget(instance)\n\n    def __set__(self, instance, value):\n        if self.fset is None:\n            raise AttributeError(\"Не можна записати: це read-only властивість\")\n        self.fset(instance, value)\n\n    def __delete__(self, instance):\n        if self.fdel is None:\n            raise AttributeError(\"Не можна видалити: делітер не визначений\")\n        self.fdel(instance)\n\n    # Методи для підтримки синтаксису @prop.setter \u002F @prop.deleter\n    def setter(self, fset):\n        \"\"\"Повертає НОВИЙ об'єкт property з доданим сеттером.\"\"\"\n        return SimplifiedProperty(self.fget, fset, self.fdel, self.__doc__)\n\n    def deleter(self, fdel):\n        \"\"\"Повертає НОВИЙ об'єкт property з доданим делітером.\"\"\"\n        return SimplifiedProperty(self.fget, self.fset, fdel, self.__doc__)\n\n\n# Тест: використання нашого SimplifiedProperty замість вбудованого\nclass TemperatureCustom:\n    def __init__(self, celsius: float):\n        self._celsius = celsius\n\n    @SimplifiedProperty\n    def celsius(self) -> float:\n        \"\"\"Температура в Цельсіях.\"\"\"\n        return self._celsius\n\n    @celsius.setter\n    def celsius(self, value: float) -> None:\n        if value \u003C -273.15:\n            raise ValueError(\"Нижче абсолютного нуля!\")\n        self._celsius = value\n\n\nt = TemperatureCustom(100.0)\nprint(t.celsius)    # 100.0 ✅\nt.celsius = 25.0\nprint(t.celsius)    # 25.0 ✅\n\ntry:\n    t.celsius = -300\nexcept ValueError as e:\n    print(f\"ValueError: {e}\")  # ✅ валідація спрацювала\n",[3399,7945,7946,7955,7959,7964,7969,7973,7977,8021,8028,8035,8042,8076,8080,8101,8106,8120,8128,8143,8156,8165,8169,8190,8205,8218,8225,8229,8246,8261,8274,8281,8285,8290,8307,8312,8337,8341,8358,8363,8387,8391,8395,8400,8409,8429,8436,8440,8445,8461,8466,8474,8478,8482,8507,8518,8532,8540,8545,8550,8561,8572,8581,8591,8596,8603,8611,8622],{"__ignoreMap":3413},[3417,7947,7948,7950,7953],{"class":3419,"line":3420},[3417,7949,3719],{"class":3718},[3417,7951,7952],{"class":3722}," SimplifiedProperty",[3417,7954,3726],{"class":3430},[3417,7956,7957],{"class":3419,"line":3427},[3417,7958,5877],{"class":3434},[3417,7960,7961],{"class":3419,"line":3448},[3417,7962,7963],{"class":3434},"    Спрощена реалізація вбудованого property на чистому Python.\n",[3417,7965,7966],{"class":3419,"line":3455},[3417,7967,7968],{"class":3434},"    Демонструє протокол дескрипторів у дії.\n",[3417,7970,7971],{"class":3419,"line":3461},[3417,7972,5877],{"class":3434},[3417,7974,7975],{"class":3419,"line":3473},[3417,7976,3452],{"emptyLinePlaceholder":3451},[3417,7978,7979,7981,7983,7985,7987,7989,7991,7993,7995,7997,7999,8001,8003,8005,8007,8009,8011,8013,8015,8017,8019],{"class":3419,"line":3485},[3417,7980,3731],{"class":3718},[3417,7982,3735],{"class":3734},[3417,7984,3738],{"class":3430},[3417,7986,3742],{"class":3741},[3417,7988,3438],{"class":3430},[3417,7990,7317],{"class":3741},[3417,7992,7334],{"class":3430},[3417,7994,3953],{"class":3718},[3417,7996,3438],{"class":3430},[3417,7998,7323],{"class":3741},[3417,8000,7334],{"class":3430},[3417,8002,3953],{"class":3718},[3417,8004,3438],{"class":3430},[3417,8006,7359],{"class":3741},[3417,8008,7334],{"class":3430},[3417,8010,3953],{"class":3718},[3417,8012,3438],{"class":3430},[3417,8014,7369],{"class":3741},[3417,8016,7334],{"class":3430},[3417,8018,3953],{"class":3718},[3417,8020,3764],{"class":3430},[3417,8022,8023,8025],{"class":3419,"line":3490},[3417,8024,3769],{"class":3718},[3417,8026,8027],{"class":3430},".fget = fget\n",[3417,8029,8030,8032],{"class":3419,"line":3496},[3417,8031,3769],{"class":3718},[3417,8033,8034],{"class":3430},".fset = fset\n",[3417,8036,8037,8039],{"class":3419,"line":3645},[3417,8038,3769],{"class":3718},[3417,8040,8041],{"class":3430},".fdel = fdel\n",[3417,8043,8044,8046,8048,8051,8054,8057,8060,8062,8065,8068,8071,8074],{"class":3419,"line":3651},[3417,8045,3769],{"class":3718},[3417,8047,3589],{"class":3430},[3417,8049,8050],{"class":3741},"__doc__",[3417,8052,8053],{"class":3430}," = doc ",[3417,8055,8056],{"class":3718},"or",[3417,8058,8059],{"class":3430}," (fget.",[3417,8061,8050],{"class":3741},[3417,8063,8064],{"class":3960}," if",[3417,8066,8067],{"class":3430}," fget ",[3417,8069,8070],{"class":3960},"else",[3417,8072,8073],{"class":3718}," None",[3417,8075,3445],{"class":3430},[3417,8077,8078],{"class":3419,"line":3657},[3417,8079,3452],{"emptyLinePlaceholder":3451},[3417,8081,8082,8084,8087,8089,8091,8093,8095,8097,8099],{"class":3419,"line":3662},[3417,8083,3731],{"class":3718},[3417,8085,8086],{"class":3734}," __get__",[3417,8088,3738],{"class":3430},[3417,8090,3742],{"class":3741},[3417,8092,3438],{"class":3430},[3417,8094,7441],{"class":3741},[3417,8096,3438],{"class":3430},[3417,8098,3871],{"class":3741},[3417,8100,3764],{"class":3430},[3417,8102,8103],{"class":3419,"line":3668},[3417,8104,8105],{"class":3423},"        # Звернення через клас (наприклад, Temperature.celsius)\n",[3417,8107,8108,8110,8113,8116,8118],{"class":3419,"line":3673},[3417,8109,3961],{"class":3960},[3417,8111,8112],{"class":3430}," instance ",[3417,8114,8115],{"class":3718},"is",[3417,8117,8073],{"class":3718},[3417,8119,3726],{"class":3430},[3417,8121,8122,8125],{"class":3419,"line":3679},[3417,8123,8124],{"class":3960},"            return",[3417,8126,8127],{"class":3718}," self\n",[3417,8129,8130,8132,8134,8137,8139,8141],{"class":3419,"line":3685},[3417,8131,3961],{"class":3960},[3417,8133,4133],{"class":3718},[3417,8135,8136],{"class":3430},".fget ",[3417,8138,8115],{"class":3718},[3417,8140,8073],{"class":3718},[3417,8142,3726],{"class":3430},[3417,8144,8145,8147,8149,8151,8154],{"class":3419,"line":4074},[3417,8146,3974],{"class":3960},[3417,8148,4491],{"class":3722},[3417,8150,3738],{"class":3430},[3417,8152,8153],{"class":3434},"\"Не можна зчитати: геттер не визначено\"",[3417,8155,3445],{"class":3430},[3417,8157,8158,8160,8162],{"class":3419,"line":4082},[3417,8159,4130],{"class":3960},[3417,8161,4133],{"class":3718},[3417,8163,8164],{"class":3430},".fget(instance)\n",[3417,8166,8167],{"class":3419,"line":4104},[3417,8168,3452],{"emptyLinePlaceholder":3451},[3417,8170,8171,8173,8176,8178,8180,8182,8184,8186,8188],{"class":3419,"line":4109},[3417,8172,3731],{"class":3718},[3417,8174,8175],{"class":3734}," __set__",[3417,8177,3738],{"class":3430},[3417,8179,3742],{"class":3741},[3417,8181,3438],{"class":3430},[3417,8183,7441],{"class":3741},[3417,8185,3438],{"class":3430},[3417,8187,5200],{"class":3741},[3417,8189,3764],{"class":3430},[3417,8191,8192,8194,8196,8199,8201,8203],{"class":3419,"line":4127},[3417,8193,3961],{"class":3960},[3417,8195,4133],{"class":3718},[3417,8197,8198],{"class":3430},".fset ",[3417,8200,8115],{"class":3718},[3417,8202,8073],{"class":3718},[3417,8204,3726],{"class":3430},[3417,8206,8207,8209,8211,8213,8216],{"class":3419,"line":4522},[3417,8208,3974],{"class":3960},[3417,8210,4491],{"class":3722},[3417,8212,3738],{"class":3430},[3417,8214,8215],{"class":3434},"\"Не можна записати: це read-only властивість\"",[3417,8217,3445],{"class":3430},[3417,8219,8220,8222],{"class":3419,"line":4527},[3417,8221,3769],{"class":3718},[3417,8223,8224],{"class":3430},".fset(instance, value)\n",[3417,8226,8227],{"class":3419,"line":4533},[3417,8228,3452],{"emptyLinePlaceholder":3451},[3417,8230,8231,8233,8236,8238,8240,8242,8244],{"class":3419,"line":4546},[3417,8232,3731],{"class":3718},[3417,8234,8235],{"class":3734}," __delete__",[3417,8237,3738],{"class":3430},[3417,8239,3742],{"class":3741},[3417,8241,3438],{"class":3430},[3417,8243,7441],{"class":3741},[3417,8245,3764],{"class":3430},[3417,8247,8248,8250,8252,8255,8257,8259],{"class":3419,"line":4551},[3417,8249,3961],{"class":3960},[3417,8251,4133],{"class":3718},[3417,8253,8254],{"class":3430},".fdel ",[3417,8256,8115],{"class":3718},[3417,8258,8073],{"class":3718},[3417,8260,3726],{"class":3430},[3417,8262,8263,8265,8267,8269,8272],{"class":3419,"line":4557},[3417,8264,3974],{"class":3960},[3417,8266,4491],{"class":3722},[3417,8268,3738],{"class":3430},[3417,8270,8271],{"class":3434},"\"Не можна видалити: делітер не визначений\"",[3417,8273,3445],{"class":3430},[3417,8275,8276,8278],{"class":3419,"line":4565},[3417,8277,3769],{"class":3718},[3417,8279,8280],{"class":3430},".fdel(instance)\n",[3417,8282,8283],{"class":3419,"line":4574},[3417,8284,3452],{"emptyLinePlaceholder":3451},[3417,8286,8287],{"class":3419,"line":5657},[3417,8288,8289],{"class":3423},"    # Методи для підтримки синтаксису @prop.setter \u002F @prop.deleter\n",[3417,8291,8292,8294,8297,8299,8301,8303,8305],{"class":3419,"line":5680},[3417,8293,3731],{"class":3718},[3417,8295,8296],{"class":3734}," setter",[3417,8298,3738],{"class":3430},[3417,8300,3742],{"class":3741},[3417,8302,3438],{"class":3430},[3417,8304,7323],{"class":3741},[3417,8306,3764],{"class":3430},[3417,8308,8309],{"class":3419,"line":5685},[3417,8310,8311],{"class":3434},"        \"\"\"Повертає НОВИЙ об'єкт property з доданим сеттером.\"\"\"\n",[3417,8313,8314,8316,8319,8321,8324,8326,8329,8331,8333,8335],{"class":3419,"line":5691},[3417,8315,4130],{"class":3960},[3417,8317,8318],{"class":3430}," SimplifiedProperty(",[3417,8320,3742],{"class":3718},[3417,8322,8323],{"class":3430},".fget, fset, ",[3417,8325,3742],{"class":3718},[3417,8327,8328],{"class":3430},".fdel, ",[3417,8330,3742],{"class":3718},[3417,8332,3589],{"class":3430},[3417,8334,8050],{"class":3741},[3417,8336,3445],{"class":3430},[3417,8338,8339],{"class":3419,"line":5700},[3417,8340,3452],{"emptyLinePlaceholder":3451},[3417,8342,8343,8345,8348,8350,8352,8354,8356],{"class":3419,"line":5722},[3417,8344,3731],{"class":3718},[3417,8346,8347],{"class":3734}," deleter",[3417,8349,3738],{"class":3430},[3417,8351,3742],{"class":3741},[3417,8353,3438],{"class":3430},[3417,8355,7359],{"class":3741},[3417,8357,3764],{"class":3430},[3417,8359,8360],{"class":3419,"line":5727},[3417,8361,8362],{"class":3434},"        \"\"\"Повертає НОВИЙ об'єкт property з доданим делітером.\"\"\"\n",[3417,8364,8365,8367,8369,8371,8374,8376,8379,8381,8383,8385],{"class":3419,"line":5733},[3417,8366,4130],{"class":3960},[3417,8368,8318],{"class":3430},[3417,8370,3742],{"class":3718},[3417,8372,8373],{"class":3430},".fget, ",[3417,8375,3742],{"class":3718},[3417,8377,8378],{"class":3430},".fset, fdel, ",[3417,8380,3742],{"class":3718},[3417,8382,3589],{"class":3430},[3417,8384,8050],{"class":3741},[3417,8386,3445],{"class":3430},[3417,8388,8389],{"class":3419,"line":5740},[3417,8390,3452],{"emptyLinePlaceholder":3451},[3417,8392,8393],{"class":3419,"line":5749},[3417,8394,3452],{"emptyLinePlaceholder":3451},[3417,8396,8397],{"class":3419,"line":5760},[3417,8398,8399],{"class":3423},"# Тест: використання нашого SimplifiedProperty замість вбудованого\n",[3417,8401,8402,8404,8407],{"class":3419,"line":6271},[3417,8403,3719],{"class":3718},[3417,8405,8406],{"class":3722}," TemperatureCustom",[3417,8408,3726],{"class":3430},[3417,8410,8411,8413,8415,8417,8419,8421,8423,8425,8427],{"class":3419,"line":6277},[3417,8412,3731],{"class":3718},[3417,8414,3735],{"class":3734},[3417,8416,3738],{"class":3430},[3417,8418,3742],{"class":3741},[3417,8420,3438],{"class":3430},[3417,8422,5910],{"class":3741},[3417,8424,3750],{"class":3430},[3417,8426,3536],{"class":3722},[3417,8428,3764],{"class":3430},[3417,8430,8431,8433],{"class":3419,"line":6289},[3417,8432,3769],{"class":3718},[3417,8434,8435],{"class":3430},"._celsius = celsius\n",[3417,8437,8438],{"class":3419,"line":6299},[3417,8439,3452],{"emptyLinePlaceholder":3451},[3417,8441,8442],{"class":3419,"line":6304},[3417,8443,8444],{"class":3734},"    @SimplifiedProperty\n",[3417,8446,8447,8449,8451,8453,8455,8457,8459],{"class":3419,"line":6310},[3417,8448,3731],{"class":3718},[3417,8450,5952],{"class":3734},[3417,8452,3738],{"class":3430},[3417,8454,3742],{"class":3741},[3417,8456,3950],{"class":3430},[3417,8458,3536],{"class":3722},[3417,8460,3726],{"class":3430},[3417,8462,8463],{"class":3419,"line":6315},[3417,8464,8465],{"class":3434},"        \"\"\"Температура в Цельсіях.\"\"\"\n",[3417,8467,8468,8470,8472],{"class":3419,"line":6322},[3417,8469,4130],{"class":3960},[3417,8471,4133],{"class":3718},[3417,8473,5976],{"class":3430},[3417,8475,8476],{"class":3419,"line":6340},[3417,8477,3452],{"emptyLinePlaceholder":3451},[3417,8479,8480],{"class":3419,"line":6346},[3417,8481,5985],{"class":3734},[3417,8483,8485,8487,8489,8491,8493,8495,8497,8499,8501,8503,8505],{"class":3419,"line":8484},52,[3417,8486,3731],{"class":3718},[3417,8488,5952],{"class":3734},[3417,8490,3738],{"class":3430},[3417,8492,3742],{"class":3741},[3417,8494,3438],{"class":3430},[3417,8496,5200],{"class":3741},[3417,8498,3750],{"class":3430},[3417,8500,3536],{"class":3722},[3417,8502,3950],{"class":3430},[3417,8504,3953],{"class":3718},[3417,8506,3726],{"class":3430},[3417,8508,8510,8512,8514,8516],{"class":3419,"line":8509},53,[3417,8511,3961],{"class":3960},[3417,8513,6075],{"class":3430},[3417,8515,6078],{"class":3441},[3417,8517,3726],{"class":3430},[3417,8519,8521,8523,8525,8527,8530],{"class":3419,"line":8520},54,[3417,8522,3974],{"class":3960},[3417,8524,3977],{"class":3722},[3417,8526,3738],{"class":3430},[3417,8528,8529],{"class":3434},"\"Нижче абсолютного нуля!\"",[3417,8531,3445],{"class":3430},[3417,8533,8535,8537],{"class":3419,"line":8534},55,[3417,8536,3769],{"class":3718},[3417,8538,8539],{"class":3430},"._celsius = value\n",[3417,8541,8543],{"class":3419,"line":8542},56,[3417,8544,3452],{"emptyLinePlaceholder":3451},[3417,8546,8548],{"class":3419,"line":8547},57,[3417,8549,3452],{"emptyLinePlaceholder":3451},[3417,8551,8553,8556,8559],{"class":3419,"line":8552},58,[3417,8554,8555],{"class":3430},"t = TemperatureCustom(",[3417,8557,8558],{"class":3441},"100.0",[3417,8560,3445],{"class":3430},[3417,8562,8564,8566,8569],{"class":3419,"line":8563},59,[3417,8565,4170],{"class":3734},[3417,8567,8568],{"class":3430},"(t.celsius)    ",[3417,8570,8571],{"class":3423},"# 100.0 ✅\n",[3417,8573,8575,8578],{"class":3419,"line":8574},60,[3417,8576,8577],{"class":3430},"t.celsius = ",[3417,8579,8580],{"class":3441},"25.0\n",[3417,8582,8584,8586,8588],{"class":3419,"line":8583},61,[3417,8585,4170],{"class":3734},[3417,8587,8568],{"class":3430},[3417,8589,8590],{"class":3423},"# 25.0 ✅\n",[3417,8592,8594],{"class":3419,"line":8593},62,[3417,8595,3452],{"emptyLinePlaceholder":3451},[3417,8597,8599,8601],{"class":3419,"line":8598},63,[3417,8600,4473],{"class":3960},[3417,8602,3726],{"class":3430},[3417,8604,8606,8608],{"class":3419,"line":8605},64,[3417,8607,6521],{"class":3430},[3417,8609,8610],{"class":3441},"300\n",[3417,8612,8614,8616,8618,8620],{"class":3419,"line":8613},65,[3417,8615,4488],{"class":3960},[3417,8617,3977],{"class":3722},[3417,8619,4494],{"class":3960},[3417,8621,4497],{"class":3430},[3417,8623,8625,8627,8629,8631,8634,8636,8638,8640,8642,8644],{"class":3419,"line":8624},66,[3417,8626,4480],{"class":3734},[3417,8628,3738],{"class":3430},[3417,8630,4001],{"class":3718},[3417,8632,8633],{"class":3434},"\"ValueError: ",[3417,8635,4007],{"class":3718},[3417,8637,4513],{"class":3430},[3417,8639,4012],{"class":3718},[3417,8641,4015],{"class":3434},[3417,8643,5264],{"class":3430},[3417,8645,8646],{"class":3423},"# ✅ валідація спрацювала\n",[4189,8648,8650,8658,8664,8670],{"title":8649},"python simplified_property_demo.py",[4193,8651,8653,4201,8656],{"className":8652},[3419],[3417,8654,4200],{"className":8655},[4199],[3516,8657,8649],{},[4193,8659,8661],{"className":8660},[3419],[3417,8662,8558],{"className":8663},[4607],[4193,8665,8667],{"className":8666},[3419],[3417,8668,6374],{"className":8669},[4607],[4193,8671,6549,8673],{"className":8672},[3419],[3417,8674,8676],{"className":8675},[4598],"Нижче абсолютного нуля!",[3560,8678,8680,8681,8683,8684,8686],{"id":8679},"незмінність-обєктів-property-чому-setter-створює-новий-обєкт","Незмінність об'єктів ",[3399,8682,7312],{},": чому ",[3399,8685,5840],{}," створює новий об'єкт",[3395,8688,8689,8690,8692,8693,8696,8697,8700,8701,8704,8705,5841,8707,8709,8710,8712],{},"Об'єкт ",[3399,8691,7312],{}," є ",[3516,8694,8695],{},"immutable-like",": виклик ",[3399,8698,8699],{},".setter(func)"," не модифікує існуючий об'єкт, а повертає ",[3516,8702,8703],{},"новий"," з скопійованими ",[3399,8706,7317],{},[3399,8708,7359],{}," і доданим ",[3399,8711,7323],{},". Доведемо практично:",[3408,8714,8716],{"className":3410,"code":8715,"language":3412,"meta":3413,"style":3413},"class Demo:\n    @property\n    def value(self) -> int:\n        return self._value\n\n    print(f\"Після @property: id={id(value)}\")   # id об'єкта 1\n\n    @value.setter\n    def value(self, v: int) -> None:\n        self._value = v\n\n    print(f\"Після @value.setter: id={id(value)}\")  # id об'єкта 2 — інший!\n",[3399,8717,8718,8727,8733,8750,8759,8763,8792,8796,8801,8826,8833,8837],{"__ignoreMap":3413},[3417,8719,8720,8722,8725],{"class":3419,"line":3420},[3417,8721,3719],{"class":3718},[3417,8723,8724],{"class":3722}," Demo",[3417,8726,3726],{"class":3430},[3417,8728,8729,8731],{"class":3419,"line":3427},[3417,8730,5452],{"class":3734},[3417,8732,5455],{"class":3722},[3417,8734,8735,8737,8740,8742,8744,8746,8748],{"class":3419,"line":3448},[3417,8736,3731],{"class":3718},[3417,8738,8739],{"class":3734}," value",[3417,8741,3738],{"class":3430},[3417,8743,3742],{"class":3741},[3417,8745,3950],{"class":3430},[3417,8747,6030],{"class":3722},[3417,8749,3726],{"class":3430},[3417,8751,8752,8754,8756],{"class":3419,"line":3455},[3417,8753,4130],{"class":3960},[3417,8755,4133],{"class":3718},[3417,8757,8758],{"class":3430},"._value\n",[3417,8760,8761],{"class":3419,"line":3461},[3417,8762,3452],{"emptyLinePlaceholder":3451},[3417,8764,8765,8767,8769,8771,8774,8776,8779,8782,8784,8786,8789],{"class":3419,"line":3473},[3417,8766,4480],{"class":3734},[3417,8768,3738],{"class":3430},[3417,8770,4001],{"class":3718},[3417,8772,8773],{"class":3434},"\"Після @property: id=",[3417,8775,4007],{"class":3718},[3417,8777,8778],{"class":3734},"id",[3417,8780,8781],{"class":3430},"(value)",[3417,8783,4012],{"class":3718},[3417,8785,4015],{"class":3434},[3417,8787,8788],{"class":3430},")   ",[3417,8790,8791],{"class":3423},"# id об'єкта 1\n",[3417,8793,8794],{"class":3419,"line":3485},[3417,8795,3452],{"emptyLinePlaceholder":3451},[3417,8797,8798],{"class":3419,"line":3490},[3417,8799,8800],{"class":3734},"    @value.setter\n",[3417,8802,8803,8805,8807,8809,8811,8813,8816,8818,8820,8822,8824],{"class":3419,"line":3496},[3417,8804,3731],{"class":3718},[3417,8806,8739],{"class":3734},[3417,8808,3738],{"class":3430},[3417,8810,3742],{"class":3741},[3417,8812,3438],{"class":3430},[3417,8814,8815],{"class":3741},"v",[3417,8817,3750],{"class":3430},[3417,8819,6030],{"class":3722},[3417,8821,3950],{"class":3430},[3417,8823,3953],{"class":3718},[3417,8825,3726],{"class":3430},[3417,8827,8828,8830],{"class":3419,"line":3645},[3417,8829,3769],{"class":3718},[3417,8831,8832],{"class":3430},"._value = v\n",[3417,8834,8835],{"class":3419,"line":3651},[3417,8836,3452],{"emptyLinePlaceholder":3451},[3417,8838,8839,8841,8843,8845,8848,8850,8852,8854,8856,8858,8860],{"class":3419,"line":3657},[3417,8840,4480],{"class":3734},[3417,8842,3738],{"class":3430},[3417,8844,4001],{"class":3718},[3417,8846,8847],{"class":3434},"\"Після @value.setter: id=",[3417,8849,4007],{"class":3718},[3417,8851,8778],{"class":3734},[3417,8853,8781],{"class":3430},[3417,8855,4012],{"class":3718},[3417,8857,4015],{"class":3434},[3417,8859,5264],{"class":3430},[3417,8861,8862],{"class":3423},"# id об'єкта 2 — інший!\n",[4189,8864,8866,8874,8886],{"title":8865},"python property_immutability.py",[4193,8867,8869,4201,8872],{"className":8868},[3419],[3417,8870,4200],{"className":8871},[4199],[3516,8873,8865],{},[4193,8875,8877,8878,4625,8882],{"className":8876},[3419],"Після @property:       id=",[3417,8879,8881],{"className":8880},[4624],"4373777600",[3417,8883,8885],{"className":8884},[4218],"# перший об'єкт",[4193,8887,8889,8890,4625,8894],{"className":8888},[3419],"Після @value.setter:  id=",[3417,8891,8893],{"className":8892},[4607],"4373777680",[3417,8895,8897],{"className":8896},[4218],"# новий об'єкт! адреса відрізняється",[4952,8899,8900,8901,8903],{},"Ця незмінність є навмисним дизайнерським рішенням. Якщо б ",[3399,8902,5840],{}," мутував існуючий об'єкт — можна було б випадково «зіпсувати» геттер, що вже використовується де-інде у коді. Immutability гарантує безпечне ланцюгування.",[3553,8905],{},[3390,8907,8909],{"id":8908},"частина-v-кастомні-дескриптори-для-перевикористання-валідації","Частина V: Кастомні дескриптори для перевикористання валідації",[3560,8911,8913,8914],{"id":8912},"проблема-десятки-однакових-property","Проблема: десятки однакових ",[3399,8915,5086],{},[3395,8917,8918],{},"Уявіть ігровий клас з десятком числових атрибутів, кожен з яким потребує валідації діапазону:",[3408,8920,8922],{"className":3410,"code":8921,"language":3412,"meta":3413,"style":3413},"# ❌ Антипатерн: дублювання логіки у кожному @property\nclass Character:\n    @property\n    def health(self) -> int:\n        return self._health\n\n    @health.setter\n    def health(self, v: int) -> None:\n        if not isinstance(v, int): raise TypeError(...)\n        if not 0 \u003C= v \u003C= 100: raise ValueError(...)\n        self._health = v\n\n    @property\n    def mana(self) -> int:       # ← та сама логіка\n        return self._mana\n\n    @mana.setter\n    def mana(self, v: int) -> None:   # ← та сама логіка\n        if not isinstance(v, int): raise TypeError(...)\n        if not 0 \u003C= v \u003C= 50: raise ValueError(...)\n        self._mana = v\n\n    # ... ще 8 атрибутів з тією ж логікою\n",[3399,8923,8924,8929,8938,8944,8961,8970,8974,8979,9003,9027,9050,9057,9061,9067,9087,9096,9100,9105,9132,9152,9173,9180,9184],{"__ignoreMap":3413},[3417,8925,8926],{"class":3419,"line":3420},[3417,8927,8928],{"class":3423},"# ❌ Антипатерн: дублювання логіки у кожному @property\n",[3417,8930,8931,8933,8936],{"class":3419,"line":3427},[3417,8932,3719],{"class":3718},[3417,8934,8935],{"class":3722}," Character",[3417,8937,3726],{"class":3430},[3417,8939,8940,8942],{"class":3419,"line":3448},[3417,8941,5452],{"class":3734},[3417,8943,5455],{"class":3722},[3417,8945,8946,8948,8951,8953,8955,8957,8959],{"class":3419,"line":3455},[3417,8947,3731],{"class":3718},[3417,8949,8950],{"class":3734}," health",[3417,8952,3738],{"class":3430},[3417,8954,3742],{"class":3741},[3417,8956,3950],{"class":3430},[3417,8958,6030],{"class":3722},[3417,8960,3726],{"class":3430},[3417,8962,8963,8965,8967],{"class":3419,"line":3461},[3417,8964,4130],{"class":3960},[3417,8966,4133],{"class":3718},[3417,8968,8969],{"class":3430},"._health\n",[3417,8971,8972],{"class":3419,"line":3473},[3417,8973,3452],{"emptyLinePlaceholder":3451},[3417,8975,8976],{"class":3419,"line":3485},[3417,8977,8978],{"class":3734},"    @health.setter\n",[3417,8980,8981,8983,8985,8987,8989,8991,8993,8995,8997,8999,9001],{"class":3419,"line":3490},[3417,8982,3731],{"class":3718},[3417,8984,8950],{"class":3734},[3417,8986,3738],{"class":3430},[3417,8988,3742],{"class":3741},[3417,8990,3438],{"class":3430},[3417,8992,8815],{"class":3741},[3417,8994,3750],{"class":3430},[3417,8996,6030],{"class":3722},[3417,8998,3950],{"class":3430},[3417,9000,3953],{"class":3718},[3417,9002,3726],{"class":3430},[3417,9004,9005,9007,9009,9011,9014,9016,9019,9022,9024],{"class":3419,"line":3496},[3417,9006,3961],{"class":3960},[3417,9008,6021],{"class":3718},[3417,9010,6024],{"class":3734},[3417,9012,9013],{"class":3430},"(v, ",[3417,9015,6030],{"class":3722},[3417,9017,9018],{"class":3430},"): ",[3417,9020,9021],{"class":3960},"raise",[3417,9023,6044],{"class":3722},[3417,9025,9026],{"class":3430},"(...)\n",[3417,9028,9029,9031,9033,9036,9039,9042,9044,9046,9048],{"class":3419,"line":3645},[3417,9030,3961],{"class":3960},[3417,9032,6021],{"class":3718},[3417,9034,9035],{"class":3441}," 0",[3417,9037,9038],{"class":3430}," \u003C= v \u003C= ",[3417,9040,9041],{"class":3441},"100",[3417,9043,3750],{"class":3430},[3417,9045,9021],{"class":3960},[3417,9047,3977],{"class":3722},[3417,9049,9026],{"class":3430},[3417,9051,9052,9054],{"class":3419,"line":3651},[3417,9053,3769],{"class":3718},[3417,9055,9056],{"class":3430},"._health = v\n",[3417,9058,9059],{"class":3419,"line":3657},[3417,9060,3452],{"emptyLinePlaceholder":3451},[3417,9062,9063,9065],{"class":3419,"line":3662},[3417,9064,5452],{"class":3734},[3417,9066,5455],{"class":3722},[3417,9068,9069,9071,9074,9076,9078,9080,9082,9084],{"class":3419,"line":3668},[3417,9070,3731],{"class":3718},[3417,9072,9073],{"class":3734}," mana",[3417,9075,3738],{"class":3430},[3417,9077,3742],{"class":3741},[3417,9079,3950],{"class":3430},[3417,9081,6030],{"class":3722},[3417,9083,5169],{"class":3430},[3417,9085,9086],{"class":3423},"# ← та сама логіка\n",[3417,9088,9089,9091,9093],{"class":3419,"line":3673},[3417,9090,4130],{"class":3960},[3417,9092,4133],{"class":3718},[3417,9094,9095],{"class":3430},"._mana\n",[3417,9097,9098],{"class":3419,"line":3679},[3417,9099,3452],{"emptyLinePlaceholder":3451},[3417,9101,9102],{"class":3419,"line":3685},[3417,9103,9104],{"class":3734},"    @mana.setter\n",[3417,9106,9107,9109,9111,9113,9115,9117,9119,9121,9123,9125,9127,9130],{"class":3419,"line":4074},[3417,9108,3731],{"class":3718},[3417,9110,9073],{"class":3734},[3417,9112,3738],{"class":3430},[3417,9114,3742],{"class":3741},[3417,9116,3438],{"class":3430},[3417,9118,8815],{"class":3741},[3417,9120,3750],{"class":3430},[3417,9122,6030],{"class":3722},[3417,9124,3950],{"class":3430},[3417,9126,3953],{"class":3718},[3417,9128,9129],{"class":3430},":   ",[3417,9131,9086],{"class":3423},[3417,9133,9134,9136,9138,9140,9142,9144,9146,9148,9150],{"class":3419,"line":4082},[3417,9135,3961],{"class":3960},[3417,9137,6021],{"class":3718},[3417,9139,6024],{"class":3734},[3417,9141,9013],{"class":3430},[3417,9143,6030],{"class":3722},[3417,9145,9018],{"class":3430},[3417,9147,9021],{"class":3960},[3417,9149,6044],{"class":3722},[3417,9151,9026],{"class":3430},[3417,9153,9154,9156,9158,9160,9162,9165,9167,9169,9171],{"class":3419,"line":4104},[3417,9155,3961],{"class":3960},[3417,9157,6021],{"class":3718},[3417,9159,9035],{"class":3441},[3417,9161,9038],{"class":3430},[3417,9163,9164],{"class":3441},"50",[3417,9166,3750],{"class":3430},[3417,9168,9021],{"class":3960},[3417,9170,3977],{"class":3722},[3417,9172,9026],{"class":3430},[3417,9174,9175,9177],{"class":3419,"line":4109},[3417,9176,3769],{"class":3718},[3417,9178,9179],{"class":3430},"._mana = v\n",[3417,9181,9182],{"class":3419,"line":4127},[3417,9183,3452],{"emptyLinePlaceholder":3451},[3417,9185,9186],{"class":3419,"line":4522},[3417,9187,9188],{"class":3423},"    # ... ще 8 атрибутів з тією ж логікою\n",[3395,9190,9191,9194],{},[3516,9192,9193],{},"Рішення:"," власний клас-дескриптор, що реалізує логіку один раз і перевикористовується для всіх атрибутів.",[3560,9196,9198,9201],{"id":9197},"__set_name__-автоматичне-отримання-імені-атрибута-python-36",[3399,9199,9200],{},"__set_name__",": автоматичне отримання імені атрибута (Python 3.6+)",[3395,9203,9204],{},"До Python 3.6 дескриптор не знав, під яким ім'ям його збережено в класі. Треба було передавати ім'я вручну:",[7020,9206,9207,9342],{},[7023,9208,9210],{"label":9209},"До Python 3.6 (застарілий)",[3408,9211,9213],{"className":3410,"code":9212,"language":3412,"meta":3413,"style":3413},"class LegacyIntegerRange:\n    def __init__(self, storage_name: str, min_val: int, max_val: int):\n        self.storage_name = storage_name  # дублювання!\n        self.min_val = min_val\n        self.max_val = max_val\n\nclass LegacyCharacter:\n    # Жахливе дублювання: ім'я вказується двічі\n    health = LegacyIntegerRange(\"_health\", 0, 100)\n    mana   = LegacyIntegerRange(\"_mana\",   0, 50)\n",[3399,9214,9215,9224,9263,9273,9280,9287,9291,9300,9305,9323],{"__ignoreMap":3413},[3417,9216,9217,9219,9222],{"class":3419,"line":3420},[3417,9218,3719],{"class":3718},[3417,9220,9221],{"class":3722}," LegacyIntegerRange",[3417,9223,3726],{"class":3430},[3417,9225,9226,9228,9230,9232,9234,9236,9239,9241,9243,9245,9248,9250,9252,9254,9257,9259,9261],{"class":3419,"line":3427},[3417,9227,3731],{"class":3718},[3417,9229,3735],{"class":3734},[3417,9231,3738],{"class":3430},[3417,9233,3742],{"class":3741},[3417,9235,3438],{"class":3430},[3417,9237,9238],{"class":3741},"storage_name",[3417,9240,3750],{"class":3430},[3417,9242,3876],{"class":3722},[3417,9244,3438],{"class":3430},[3417,9246,9247],{"class":3741},"min_val",[3417,9249,3750],{"class":3430},[3417,9251,6030],{"class":3722},[3417,9253,3438],{"class":3430},[3417,9255,9256],{"class":3741},"max_val",[3417,9258,3750],{"class":3430},[3417,9260,6030],{"class":3722},[3417,9262,3764],{"class":3430},[3417,9264,9265,9267,9270],{"class":3419,"line":3448},[3417,9266,3769],{"class":3718},[3417,9268,9269],{"class":3430},".storage_name = storage_name  ",[3417,9271,9272],{"class":3423},"# дублювання!\n",[3417,9274,9275,9277],{"class":3419,"line":3455},[3417,9276,3769],{"class":3718},[3417,9278,9279],{"class":3430},".min_val = min_val\n",[3417,9281,9282,9284],{"class":3419,"line":3461},[3417,9283,3769],{"class":3718},[3417,9285,9286],{"class":3430},".max_val = max_val\n",[3417,9288,9289],{"class":3419,"line":3473},[3417,9290,3452],{"emptyLinePlaceholder":3451},[3417,9292,9293,9295,9298],{"class":3419,"line":3485},[3417,9294,3719],{"class":3718},[3417,9296,9297],{"class":3722}," LegacyCharacter",[3417,9299,3726],{"class":3430},[3417,9301,9302],{"class":3419,"line":3490},[3417,9303,9304],{"class":3423},"    # Жахливе дублювання: ім'я вказується двічі\n",[3417,9306,9307,9310,9313,9315,9317,9319,9321],{"class":3419,"line":3496},[3417,9308,9309],{"class":3430},"    health = LegacyIntegerRange(",[3417,9311,9312],{"class":3434},"\"_health\"",[3417,9314,3438],{"class":3430},[3417,9316,3967],{"class":3441},[3417,9318,3438],{"class":3430},[3417,9320,9041],{"class":3441},[3417,9322,3445],{"class":3430},[3417,9324,9325,9328,9331,9334,9336,9338,9340],{"class":3419,"line":3645},[3417,9326,9327],{"class":3430},"    mana   = LegacyIntegerRange(",[3417,9329,9330],{"class":3434},"\"_mana\"",[3417,9332,9333],{"class":3430},",   ",[3417,9335,3967],{"class":3441},[3417,9337,3438],{"class":3430},[3417,9339,9164],{"class":3441},[3417,9341,3445],{"class":3430},[7023,9343,9345],{"label":9344},"Python 3.6+ з __set_name__",[3408,9346,9348],{"className":3410,"code":9347,"language":3412,"meta":3413,"style":3413},"class IntegerRange:\n    def __init__(self, min_val: int, max_val: int):\n        self.min_val = min_val\n        self.max_val = max_val\n\n    def __set_name__(self, owner, name):\n        # Python сам повідомляє дескриптору його ім'я!\n        # owner = Character, name = \"health\"\n        self.public_name  = name          # \"health\"\n        self.private_name = f\"_{name}\"    # \"_health\"\n\nclass Character:\n    # Жодного дублювання!\n    health = IntegerRange(0, 100)\n    mana   = IntegerRange(0, 50)\n",[3399,9349,9350,9359,9387,9393,9399,9403,9425,9430,9435,9445,9468,9472,9480,9485,9498],{"__ignoreMap":3413},[3417,9351,9352,9354,9357],{"class":3419,"line":3420},[3417,9353,3719],{"class":3718},[3417,9355,9356],{"class":3722}," IntegerRange",[3417,9358,3726],{"class":3430},[3417,9360,9361,9363,9365,9367,9369,9371,9373,9375,9377,9379,9381,9383,9385],{"class":3419,"line":3427},[3417,9362,3731],{"class":3718},[3417,9364,3735],{"class":3734},[3417,9366,3738],{"class":3430},[3417,9368,3742],{"class":3741},[3417,9370,3438],{"class":3430},[3417,9372,9247],{"class":3741},[3417,9374,3750],{"class":3430},[3417,9376,6030],{"class":3722},[3417,9378,3438],{"class":3430},[3417,9380,9256],{"class":3741},[3417,9382,3750],{"class":3430},[3417,9384,6030],{"class":3722},[3417,9386,3764],{"class":3430},[3417,9388,9389,9391],{"class":3419,"line":3448},[3417,9390,3769],{"class":3718},[3417,9392,9279],{"class":3430},[3417,9394,9395,9397],{"class":3419,"line":3455},[3417,9396,3769],{"class":3718},[3417,9398,9286],{"class":3430},[3417,9400,9401],{"class":3419,"line":3461},[3417,9402,3452],{"emptyLinePlaceholder":3451},[3417,9404,9405,9407,9410,9412,9414,9416,9418,9420,9423],{"class":3419,"line":3473},[3417,9406,3731],{"class":3718},[3417,9408,9409],{"class":3734}," __set_name__",[3417,9411,3738],{"class":3430},[3417,9413,3742],{"class":3741},[3417,9415,3438],{"class":3430},[3417,9417,3871],{"class":3741},[3417,9419,3438],{"class":3430},[3417,9421,9422],{"class":3741},"name",[3417,9424,3764],{"class":3430},[3417,9426,9427],{"class":3419,"line":3485},[3417,9428,9429],{"class":3423},"        # Python сам повідомляє дескриптору його ім'я!\n",[3417,9431,9432],{"class":3419,"line":3490},[3417,9433,9434],{"class":3423},"        # owner = Character, name = \"health\"\n",[3417,9436,9437,9439,9442],{"class":3419,"line":3496},[3417,9438,3769],{"class":3718},[3417,9440,9441],{"class":3430},".public_name  = name          ",[3417,9443,9444],{"class":3423},"# \"health\"\n",[3417,9446,9447,9449,9452,9454,9457,9459,9461,9463,9465],{"class":3419,"line":3645},[3417,9448,3769],{"class":3718},[3417,9450,9451],{"class":3430},".private_name = ",[3417,9453,4001],{"class":3718},[3417,9455,9456],{"class":3434},"\"_",[3417,9458,4007],{"class":3718},[3417,9460,9422],{"class":3430},[3417,9462,4012],{"class":3718},[3417,9464,4015],{"class":3434},[3417,9466,9467],{"class":3423},"    # \"_health\"\n",[3417,9469,9470],{"class":3419,"line":3651},[3417,9471,3452],{"emptyLinePlaceholder":3451},[3417,9473,9474,9476,9478],{"class":3419,"line":3657},[3417,9475,3719],{"class":3718},[3417,9477,8935],{"class":3722},[3417,9479,3726],{"class":3430},[3417,9481,9482],{"class":3419,"line":3662},[3417,9483,9484],{"class":3423},"    # Жодного дублювання!\n",[3417,9486,9487,9490,9492,9494,9496],{"class":3419,"line":3668},[3417,9488,9489],{"class":3430},"    health = IntegerRange(",[3417,9491,3967],{"class":3441},[3417,9493,3438],{"class":3430},[3417,9495,9041],{"class":3441},[3417,9497,3445],{"class":3430},[3417,9499,9500,9503,9505,9507,9509],{"class":3419,"line":3673},[3417,9501,9502],{"class":3430},"    mana   = IntegerRange(",[3417,9504,3967],{"class":3441},[3417,9506,3438],{"class":3430},[3417,9508,9164],{"class":3441},[3417,9510,3445],{"class":3430},[3560,9512,9514,9515,9518],{"id":9513},"повна-реалізація-integerrange-дескриптора","Повна реалізація ",[3399,9516,9517],{},"IntegerRange"," дескриптора",[3408,9520,9522],{"className":3410,"code":9521,"language":3412,"meta":3413,"style":3413},"# descriptors_validation.py\n\nclass IntegerRange:\n    \"\"\"\n    Data Descriptor для валідації цілочислових атрибутів у діапазоні.\n    Автоматично отримує ім'я атрибута через __set_name__ (Python 3.6+).\n    \"\"\"\n\n    def __init__(self, min_value: int, max_value: int):\n        self.min_value = min_value\n        self.max_value = max_value\n        # Ці поля будуть заповнені __set_name__\n        self.public_name  = None\n        self.private_name = None\n\n    def __set_name__(self, owner: type, name: str) -> None:\n        \"\"\"Викликається Python при парсингу класу-власника.\"\"\"\n        self.public_name  = name\n        self.private_name = f\"_{name}\"\n\n    def __get__(self, instance, owner: type):\n        if instance is None:\n            return self   # доступ через клас → повертаємо дескриптор\n        return getattr(instance, self.private_name, 0)\n\n    def __set__(self, instance, value: int) -> None:\n        # 1. Перевірка типу\n        if not isinstance(value, int):\n            raise TypeError(\n                f\"'{self.public_name}' має бути int, \"\n                f\"отримано {type(value).__name__!r}\"\n            )\n        # 2. Перевірка діапазону\n        if not (self.min_value \u003C= value \u003C= self.max_value):\n            raise ValueError(\n                f\"'{self.public_name}' має бути у [{self.min_value}, {self.max_value}], \"\n                f\"отримано {value}\"\n            )\n        # 3. Запис у захищений атрибут екземпляра\n        setattr(instance, self.private_name, value)\n\n    def __repr__(self) -> str:\n        return (\n            f\"IntegerRange({self.min_value!r}, {self.max_value!r}) \"\n            f\"→ '{self.public_name}'\"\n        )\n\n\nclass Character:\n    \"\"\"Ігровий персонаж з валідованими атрибутами через дескриптори.\"\"\"\n\n    health = IntegerRange(0, 100)\n    mana   = IntegerRange(0, 50)\n    level  = IntegerRange(1, 80)\n    armor  = IntegerRange(0, 500)\n\n    def __init__(self, name: str, health: int, mana: int, level: int, armor: int = 0):\n        self.name   = name\n        self.health = health   # → IntegerRange.__set__ з валідацією\n        self.mana   = mana\n        self.level  = level\n        self.armor  = armor\n\n    def __repr__(self) -> str:\n        return (\n            f\"Character({self.name!r}, HP={self.health}, \"\n            f\"MP={self.mana}, LVL={self.level}, ARM={self.armor})\"\n        )\n\n\n# ── Тести ─────────────────────────────────────────────────────────────────────\nhero = Character(\"Арагорн\", 100, 30, 5)\nprint(hero)\n\nhero.health = 75\nprint(f\"HP після удару: {hero.health}\")\n\n# Дескриптори у __dict__ класу — не у __dict__ екземпляра!\nprint(f\"\\nCharacter.__dict__ keys: {[k for k in Character.__dict__ if not k.startswith('__')]}\")\nprint(f\"hero.__dict__: {hero.__dict__}\")\n\n# Метаінформація дескриптора\nprint(f\"\\nОпис health: {Character.health}\")\n\n# Помилки\ntests = [\n    (lambda: setattr(hero, 'health', 150),  \"health=150 (>100)\"),\n    (lambda: setattr(hero, 'mana', -1),     \"mana=-1 (\u003C0)\"),\n    (lambda: setattr(hero, 'level', \"max\"), \"level='max' (не int)\"),\n]\n\nfor fn, label in tests:\n    try:\n        fn()\n    except (ValueError, TypeError) as e:\n        print(f\"[{label}] {type(e).__name__}: {e}\")\n",[3399,9523,9524,9529,9533,9541,9545,9550,9555,9559,9563,9593,9600,9607,9612,9622,9630,9634,9666,9671,9678,9697,9701,9725,9737,9746,9765,9769,9797,9802,9817,9826,9844,9864,9869,9874,9892,9900,9934,9948,9952,9957,9969,9973,9990,9997,10022,10038,10043,10047,10051,10059,10064,10068,10080,10092,10107,10120,10124,10185,10192,10202,10209,10216,10223,10227,10243,10249,10276,10314,10319,10324,10329,10335,10358,10366,10371,10380,10403,10408,10414,10468,10493,10498,10504,10531,10536,10542,10548,10582,10611,10640,10646,10651,10664,10672,10678,10702],{"__ignoreMap":3413},[3417,9525,9526],{"class":3419,"line":3420},[3417,9527,9528],{"class":3423},"# descriptors_validation.py\n",[3417,9530,9531],{"class":3419,"line":3427},[3417,9532,3452],{"emptyLinePlaceholder":3451},[3417,9534,9535,9537,9539],{"class":3419,"line":3448},[3417,9536,3719],{"class":3718},[3417,9538,9356],{"class":3722},[3417,9540,3726],{"class":3430},[3417,9542,9543],{"class":3419,"line":3455},[3417,9544,5877],{"class":3434},[3417,9546,9547],{"class":3419,"line":3461},[3417,9548,9549],{"class":3434},"    Data Descriptor для валідації цілочислових атрибутів у діапазоні.\n",[3417,9551,9552],{"class":3419,"line":3473},[3417,9553,9554],{"class":3434},"    Автоматично отримує ім'я атрибута через __set_name__ (Python 3.6+).\n",[3417,9556,9557],{"class":3419,"line":3485},[3417,9558,5877],{"class":3434},[3417,9560,9561],{"class":3419,"line":3490},[3417,9562,3452],{"emptyLinePlaceholder":3451},[3417,9564,9565,9567,9569,9571,9573,9575,9578,9580,9582,9584,9587,9589,9591],{"class":3419,"line":3496},[3417,9566,3731],{"class":3718},[3417,9568,3735],{"class":3734},[3417,9570,3738],{"class":3430},[3417,9572,3742],{"class":3741},[3417,9574,3438],{"class":3430},[3417,9576,9577],{"class":3741},"min_value",[3417,9579,3750],{"class":3430},[3417,9581,6030],{"class":3722},[3417,9583,3438],{"class":3430},[3417,9585,9586],{"class":3741},"max_value",[3417,9588,3750],{"class":3430},[3417,9590,6030],{"class":3722},[3417,9592,3764],{"class":3430},[3417,9594,9595,9597],{"class":3419,"line":3645},[3417,9596,3769],{"class":3718},[3417,9598,9599],{"class":3430},".min_value = min_value\n",[3417,9601,9602,9604],{"class":3419,"line":3651},[3417,9603,3769],{"class":3718},[3417,9605,9606],{"class":3430},".max_value = max_value\n",[3417,9608,9609],{"class":3419,"line":3657},[3417,9610,9611],{"class":3423},"        # Ці поля будуть заповнені __set_name__\n",[3417,9613,9614,9616,9619],{"class":3419,"line":3662},[3417,9615,3769],{"class":3718},[3417,9617,9618],{"class":3430},".public_name  = ",[3417,9620,9621],{"class":3718},"None\n",[3417,9623,9624,9626,9628],{"class":3419,"line":3668},[3417,9625,3769],{"class":3718},[3417,9627,9451],{"class":3430},[3417,9629,9621],{"class":3718},[3417,9631,9632],{"class":3419,"line":3673},[3417,9633,3452],{"emptyLinePlaceholder":3451},[3417,9635,9636,9638,9640,9642,9644,9646,9648,9650,9652,9654,9656,9658,9660,9662,9664],{"class":3419,"line":3679},[3417,9637,3731],{"class":3718},[3417,9639,9409],{"class":3734},[3417,9641,3738],{"class":3430},[3417,9643,3742],{"class":3741},[3417,9645,3438],{"class":3430},[3417,9647,3871],{"class":3741},[3417,9649,3750],{"class":3430},[3417,9651,6056],{"class":3722},[3417,9653,3438],{"class":3430},[3417,9655,9422],{"class":3741},[3417,9657,3750],{"class":3430},[3417,9659,3876],{"class":3722},[3417,9661,3950],{"class":3430},[3417,9663,3953],{"class":3718},[3417,9665,3726],{"class":3430},[3417,9667,9668],{"class":3419,"line":3685},[3417,9669,9670],{"class":3434},"        \"\"\"Викликається Python при парсингу класу-власника.\"\"\"\n",[3417,9672,9673,9675],{"class":3419,"line":4074},[3417,9674,3769],{"class":3718},[3417,9676,9677],{"class":3430},".public_name  = name\n",[3417,9679,9680,9682,9684,9686,9688,9690,9692,9694],{"class":3419,"line":4082},[3417,9681,3769],{"class":3718},[3417,9683,9451],{"class":3430},[3417,9685,4001],{"class":3718},[3417,9687,9456],{"class":3434},[3417,9689,4007],{"class":3718},[3417,9691,9422],{"class":3430},[3417,9693,4012],{"class":3718},[3417,9695,9696],{"class":3434},"\"\n",[3417,9698,9699],{"class":3419,"line":4104},[3417,9700,3452],{"emptyLinePlaceholder":3451},[3417,9702,9703,9705,9707,9709,9711,9713,9715,9717,9719,9721,9723],{"class":3419,"line":4109},[3417,9704,3731],{"class":3718},[3417,9706,8086],{"class":3734},[3417,9708,3738],{"class":3430},[3417,9710,3742],{"class":3741},[3417,9712,3438],{"class":3430},[3417,9714,7441],{"class":3741},[3417,9716,3438],{"class":3430},[3417,9718,3871],{"class":3741},[3417,9720,3750],{"class":3430},[3417,9722,6056],{"class":3722},[3417,9724,3764],{"class":3430},[3417,9726,9727,9729,9731,9733,9735],{"class":3419,"line":4127},[3417,9728,3961],{"class":3960},[3417,9730,8112],{"class":3430},[3417,9732,8115],{"class":3718},[3417,9734,8073],{"class":3718},[3417,9736,3726],{"class":3430},[3417,9738,9739,9741,9743],{"class":3419,"line":4522},[3417,9740,8124],{"class":3960},[3417,9742,4133],{"class":3718},[3417,9744,9745],{"class":3423},"   # доступ через клас → повертаємо дескриптор\n",[3417,9747,9748,9750,9753,9756,9758,9761,9763],{"class":3419,"line":4527},[3417,9749,4130],{"class":3960},[3417,9751,9752],{"class":3734}," getattr",[3417,9754,9755],{"class":3430},"(instance, ",[3417,9757,3742],{"class":3718},[3417,9759,9760],{"class":3430},".private_name, ",[3417,9762,3967],{"class":3441},[3417,9764,3445],{"class":3430},[3417,9766,9767],{"class":3419,"line":4533},[3417,9768,3452],{"emptyLinePlaceholder":3451},[3417,9770,9771,9773,9775,9777,9779,9781,9783,9785,9787,9789,9791,9793,9795],{"class":3419,"line":4546},[3417,9772,3731],{"class":3718},[3417,9774,8175],{"class":3734},[3417,9776,3738],{"class":3430},[3417,9778,3742],{"class":3741},[3417,9780,3438],{"class":3430},[3417,9782,7441],{"class":3741},[3417,9784,3438],{"class":3430},[3417,9786,5200],{"class":3741},[3417,9788,3750],{"class":3430},[3417,9790,6030],{"class":3722},[3417,9792,3950],{"class":3430},[3417,9794,3953],{"class":3718},[3417,9796,3726],{"class":3430},[3417,9798,9799],{"class":3419,"line":4551},[3417,9800,9801],{"class":3423},"        # 1. Перевірка типу\n",[3417,9803,9804,9806,9808,9810,9813,9815],{"class":3419,"line":4557},[3417,9805,3961],{"class":3960},[3417,9807,6021],{"class":3718},[3417,9809,6024],{"class":3734},[3417,9811,9812],{"class":3430},"(value, ",[3417,9814,6030],{"class":3722},[3417,9816,3764],{"class":3430},[3417,9818,9819,9821,9823],{"class":3419,"line":4565},[3417,9820,3974],{"class":3960},[3417,9822,6044],{"class":3722},[3417,9824,9825],{"class":3430},"(\n",[3417,9827,9828,9831,9834,9836,9839,9841],{"class":3419,"line":4574},[3417,9829,9830],{"class":3718},"                f",[3417,9832,9833],{"class":3434},"\"'",[3417,9835,4744],{"class":3718},[3417,9837,9838],{"class":3430},".public_name",[3417,9840,4012],{"class":3718},[3417,9842,9843],{"class":3434},"' має бути int, \"\n",[3417,9845,9846,9848,9851,9853,9855,9857,9859,9862],{"class":3419,"line":5657},[3417,9847,9830],{"class":3718},[3417,9849,9850],{"class":3434},"\"отримано ",[3417,9852,4007],{"class":3718},[3417,9854,6056],{"class":3722},[3417,9856,6059],{"class":3430},[3417,9858,6062],{"class":3741},[3417,9860,9861],{"class":3718},"!r}",[3417,9863,9696],{"class":3434},[3417,9865,9866],{"class":3419,"line":5680},[3417,9867,9868],{"class":3430},"            )\n",[3417,9870,9871],{"class":3419,"line":5685},[3417,9872,9873],{"class":3423},"        # 2. Перевірка діапазону\n",[3417,9875,9876,9878,9880,9882,9884,9887,9889],{"class":3419,"line":5691},[3417,9877,3961],{"class":3960},[3417,9879,6021],{"class":3718},[3417,9881,6164],{"class":3430},[3417,9883,3742],{"class":3718},[3417,9885,9886],{"class":3430},".min_value \u003C= value \u003C= ",[3417,9888,3742],{"class":3718},[3417,9890,9891],{"class":3430},".max_value):\n",[3417,9893,9894,9896,9898],{"class":3419,"line":5700},[3417,9895,3974],{"class":3960},[3417,9897,3977],{"class":3722},[3417,9899,9825],{"class":3430},[3417,9901,9902,9904,9906,9908,9910,9912,9915,9917,9920,9922,9924,9926,9929,9931],{"class":3419,"line":5722},[3417,9903,9830],{"class":3718},[3417,9905,9833],{"class":3434},[3417,9907,4744],{"class":3718},[3417,9909,9838],{"class":3430},[3417,9911,4012],{"class":3718},[3417,9913,9914],{"class":3434},"' має бути у [",[3417,9916,4744],{"class":3718},[3417,9918,9919],{"class":3430},".min_value",[3417,9921,4012],{"class":3718},[3417,9923,3438],{"class":3434},[3417,9925,4744],{"class":3718},[3417,9927,9928],{"class":3430},".max_value",[3417,9930,4012],{"class":3718},[3417,9932,9933],{"class":3434},"], \"\n",[3417,9935,9936,9938,9940,9942,9944,9946],{"class":3419,"line":5727},[3417,9937,9830],{"class":3718},[3417,9939,9850],{"class":3434},[3417,9941,4007],{"class":3718},[3417,9943,5200],{"class":3430},[3417,9945,4012],{"class":3718},[3417,9947,9696],{"class":3434},[3417,9949,9950],{"class":3419,"line":5733},[3417,9951,9868],{"class":3430},[3417,9953,9954],{"class":3419,"line":5740},[3417,9955,9956],{"class":3423},"        # 3. Запис у захищений атрибут екземпляра\n",[3417,9958,9959,9962,9964,9966],{"class":3419,"line":5749},[3417,9960,9961],{"class":3734},"        setattr",[3417,9963,9755],{"class":3430},[3417,9965,3742],{"class":3718},[3417,9967,9968],{"class":3430},".private_name, value)\n",[3417,9970,9971],{"class":3419,"line":5760},[3417,9972,3452],{"emptyLinePlaceholder":3451},[3417,9974,9975,9977,9980,9982,9984,9986,9988],{"class":3419,"line":6271},[3417,9976,3731],{"class":3718},[3417,9978,9979],{"class":3734}," __repr__",[3417,9981,3738],{"class":3430},[3417,9983,3742],{"class":3741},[3417,9985,3950],{"class":3430},[3417,9987,3876],{"class":3722},[3417,9989,3726],{"class":3430},[3417,9991,9992,9994],{"class":3419,"line":6277},[3417,9993,4130],{"class":3960},[3417,9995,9996],{"class":3430}," (\n",[3417,9998,9999,10002,10005,10007,10009,10011,10013,10015,10017,10019],{"class":3419,"line":6289},[3417,10000,10001],{"class":3718},"            f",[3417,10003,10004],{"class":3434},"\"IntegerRange(",[3417,10006,4744],{"class":3718},[3417,10008,9919],{"class":3430},[3417,10010,9861],{"class":3718},[3417,10012,3438],{"class":3434},[3417,10014,4744],{"class":3718},[3417,10016,9928],{"class":3430},[3417,10018,9861],{"class":3718},[3417,10020,10021],{"class":3434},") \"\n",[3417,10023,10024,10026,10029,10031,10033,10035],{"class":3419,"line":6299},[3417,10025,10001],{"class":3718},[3417,10027,10028],{"class":3434},"\"→ '",[3417,10030,4744],{"class":3718},[3417,10032,9838],{"class":3430},[3417,10034,4012],{"class":3718},[3417,10036,10037],{"class":3434},"'\"\n",[3417,10039,10040],{"class":3419,"line":6304},[3417,10041,10042],{"class":3430},"        )\n",[3417,10044,10045],{"class":3419,"line":6310},[3417,10046,3452],{"emptyLinePlaceholder":3451},[3417,10048,10049],{"class":3419,"line":6315},[3417,10050,3452],{"emptyLinePlaceholder":3451},[3417,10052,10053,10055,10057],{"class":3419,"line":6322},[3417,10054,3719],{"class":3718},[3417,10056,8935],{"class":3722},[3417,10058,3726],{"class":3430},[3417,10060,10061],{"class":3419,"line":6340},[3417,10062,10063],{"class":3434},"    \"\"\"Ігровий персонаж з валідованими атрибутами через дескриптори.\"\"\"\n",[3417,10065,10066],{"class":3419,"line":6346},[3417,10067,3452],{"emptyLinePlaceholder":3451},[3417,10069,10070,10072,10074,10076,10078],{"class":3419,"line":8484},[3417,10071,9489],{"class":3430},[3417,10073,3967],{"class":3441},[3417,10075,3438],{"class":3430},[3417,10077,9041],{"class":3441},[3417,10079,3445],{"class":3430},[3417,10081,10082,10084,10086,10088,10090],{"class":3419,"line":8509},[3417,10083,9502],{"class":3430},[3417,10085,3967],{"class":3441},[3417,10087,3438],{"class":3430},[3417,10089,9164],{"class":3441},[3417,10091,3445],{"class":3430},[3417,10093,10094,10097,10100,10102,10105],{"class":3419,"line":8520},[3417,10095,10096],{"class":3430},"    level  = IntegerRange(",[3417,10098,10099],{"class":3441},"1",[3417,10101,3438],{"class":3430},[3417,10103,10104],{"class":3441},"80",[3417,10106,3445],{"class":3430},[3417,10108,10109,10112,10114,10116,10118],{"class":3419,"line":8534},[3417,10110,10111],{"class":3430},"    armor  = IntegerRange(",[3417,10113,3967],{"class":3441},[3417,10115,3438],{"class":3430},[3417,10117,5261],{"class":3441},[3417,10119,3445],{"class":3430},[3417,10121,10122],{"class":3419,"line":8542},[3417,10123,3452],{"emptyLinePlaceholder":3451},[3417,10125,10126,10128,10130,10132,10134,10136,10138,10140,10142,10144,10147,10149,10151,10153,10156,10158,10160,10162,10165,10167,10169,10171,10174,10176,10178,10181,10183],{"class":3419,"line":8547},[3417,10127,3731],{"class":3718},[3417,10129,3735],{"class":3734},[3417,10131,3738],{"class":3430},[3417,10133,3742],{"class":3741},[3417,10135,3438],{"class":3430},[3417,10137,9422],{"class":3741},[3417,10139,3750],{"class":3430},[3417,10141,3876],{"class":3722},[3417,10143,3438],{"class":3430},[3417,10145,10146],{"class":3741},"health",[3417,10148,3750],{"class":3430},[3417,10150,6030],{"class":3722},[3417,10152,3438],{"class":3430},[3417,10154,10155],{"class":3741},"mana",[3417,10157,3750],{"class":3430},[3417,10159,6030],{"class":3722},[3417,10161,3438],{"class":3430},[3417,10163,10164],{"class":3741},"level",[3417,10166,3750],{"class":3430},[3417,10168,6030],{"class":3722},[3417,10170,3438],{"class":3430},[3417,10172,10173],{"class":3741},"armor",[3417,10175,3750],{"class":3430},[3417,10177,6030],{"class":3722},[3417,10179,10180],{"class":3430}," = ",[3417,10182,3967],{"class":3441},[3417,10184,3764],{"class":3430},[3417,10186,10187,10189],{"class":3419,"line":8552},[3417,10188,3769],{"class":3718},[3417,10190,10191],{"class":3430},".name   = name\n",[3417,10193,10194,10196,10199],{"class":3419,"line":8563},[3417,10195,3769],{"class":3718},[3417,10197,10198],{"class":3430},".health = health   ",[3417,10200,10201],{"class":3423},"# → IntegerRange.__set__ з валідацією\n",[3417,10203,10204,10206],{"class":3419,"line":8574},[3417,10205,3769],{"class":3718},[3417,10207,10208],{"class":3430},".mana   = mana\n",[3417,10210,10211,10213],{"class":3419,"line":8583},[3417,10212,3769],{"class":3718},[3417,10214,10215],{"class":3430},".level  = level\n",[3417,10217,10218,10220],{"class":3419,"line":8593},[3417,10219,3769],{"class":3718},[3417,10221,10222],{"class":3430},".armor  = armor\n",[3417,10224,10225],{"class":3419,"line":8598},[3417,10226,3452],{"emptyLinePlaceholder":3451},[3417,10228,10229,10231,10233,10235,10237,10239,10241],{"class":3419,"line":8605},[3417,10230,3731],{"class":3718},[3417,10232,9979],{"class":3734},[3417,10234,3738],{"class":3430},[3417,10236,3742],{"class":3741},[3417,10238,3950],{"class":3430},[3417,10240,3876],{"class":3722},[3417,10242,3726],{"class":3430},[3417,10244,10245,10247],{"class":3419,"line":8613},[3417,10246,4130],{"class":3960},[3417,10248,9996],{"class":3430},[3417,10250,10251,10253,10256,10258,10261,10263,10266,10268,10271,10273],{"class":3419,"line":8624},[3417,10252,10001],{"class":3718},[3417,10254,10255],{"class":3434},"\"Character(",[3417,10257,4744],{"class":3718},[3417,10259,10260],{"class":3430},".name",[3417,10262,9861],{"class":3718},[3417,10264,10265],{"class":3434},", HP=",[3417,10267,4744],{"class":3718},[3417,10269,10270],{"class":3430},".health",[3417,10272,4012],{"class":3718},[3417,10274,10275],{"class":3434},", \"\n",[3417,10277,10279,10281,10284,10286,10289,10291,10294,10296,10299,10301,10304,10306,10309,10311],{"class":3419,"line":10278},67,[3417,10280,10001],{"class":3718},[3417,10282,10283],{"class":3434},"\"MP=",[3417,10285,4744],{"class":3718},[3417,10287,10288],{"class":3430},".mana",[3417,10290,4012],{"class":3718},[3417,10292,10293],{"class":3434},", LVL=",[3417,10295,4744],{"class":3718},[3417,10297,10298],{"class":3430},".level",[3417,10300,4012],{"class":3718},[3417,10302,10303],{"class":3434},", ARM=",[3417,10305,4744],{"class":3718},[3417,10307,10308],{"class":3430},".armor",[3417,10310,4012],{"class":3718},[3417,10312,10313],{"class":3434},")\"\n",[3417,10315,10317],{"class":3419,"line":10316},68,[3417,10318,10042],{"class":3430},[3417,10320,10322],{"class":3419,"line":10321},69,[3417,10323,3452],{"emptyLinePlaceholder":3451},[3417,10325,10327],{"class":3419,"line":10326},70,[3417,10328,3452],{"emptyLinePlaceholder":3451},[3417,10330,10332],{"class":3419,"line":10331},71,[3417,10333,10334],{"class":3423},"# ── Тести ─────────────────────────────────────────────────────────────────────\n",[3417,10336,10338,10341,10344,10346,10348,10350,10352,10354,10356],{"class":3419,"line":10337},72,[3417,10339,10340],{"class":3430},"hero = Character(",[3417,10342,10343],{"class":3434},"\"Арагорн\"",[3417,10345,3438],{"class":3430},[3417,10347,9041],{"class":3441},[3417,10349,3438],{"class":3430},[3417,10351,4709],{"class":3441},[3417,10353,3438],{"class":3430},[3417,10355,6177],{"class":3441},[3417,10357,3445],{"class":3430},[3417,10359,10361,10363],{"class":3419,"line":10360},73,[3417,10362,4170],{"class":3734},[3417,10364,10365],{"class":3430},"(hero)\n",[3417,10367,10369],{"class":3419,"line":10368},74,[3417,10370,3452],{"emptyLinePlaceholder":3451},[3417,10372,10374,10377],{"class":3419,"line":10373},75,[3417,10375,10376],{"class":3430},"hero.health = ",[3417,10378,10379],{"class":3441},"75\n",[3417,10381,10383,10385,10387,10389,10392,10394,10397,10399,10401],{"class":3419,"line":10382},76,[3417,10384,4170],{"class":3734},[3417,10386,3738],{"class":3430},[3417,10388,4001],{"class":3718},[3417,10390,10391],{"class":3434},"\"HP після удару: ",[3417,10393,4007],{"class":3718},[3417,10395,10396],{"class":3430},"hero.health",[3417,10398,4012],{"class":3718},[3417,10400,4015],{"class":3434},[3417,10402,3445],{"class":3430},[3417,10404,10406],{"class":3419,"line":10405},77,[3417,10407,3452],{"emptyLinePlaceholder":3451},[3417,10409,10411],{"class":3419,"line":10410},78,[3417,10412,10413],{"class":3423},"# Дескриптори у __dict__ класу — не у __dict__ екземпляра!\n",[3417,10415,10417,10419,10421,10423,10425,10427,10430,10432,10435,10438,10441,10444,10447,10449,10451,10453,10456,10459,10462,10464,10466],{"class":3419,"line":10416},79,[3417,10418,4170],{"class":3734},[3417,10420,3738],{"class":3430},[3417,10422,4001],{"class":3718},[3417,10424,4015],{"class":3434},[3417,10426,6476],{"class":6475},[3417,10428,10429],{"class":3434},"Character.__dict__ keys: ",[3417,10431,4007],{"class":3718},[3417,10433,10434],{"class":3430},"[k ",[3417,10436,10437],{"class":3960},"for",[3417,10439,10440],{"class":3430}," k ",[3417,10442,10443],{"class":3960},"in",[3417,10445,10446],{"class":3430}," Character.",[3417,10448,4541],{"class":3741},[3417,10450,8064],{"class":3960},[3417,10452,6021],{"class":3718},[3417,10454,10455],{"class":3430}," k.startswith(",[3417,10457,10458],{"class":3434},"'__'",[3417,10460,10461],{"class":3430},")]",[3417,10463,4012],{"class":3718},[3417,10465,4015],{"class":3434},[3417,10467,3445],{"class":3430},[3417,10469,10471,10473,10475,10477,10480,10482,10485,10487,10489,10491],{"class":3419,"line":10470},80,[3417,10472,4170],{"class":3734},[3417,10474,3738],{"class":3430},[3417,10476,4001],{"class":3718},[3417,10478,10479],{"class":3434},"\"hero.__dict__: ",[3417,10481,4007],{"class":3718},[3417,10483,10484],{"class":3430},"hero.",[3417,10486,4541],{"class":3741},[3417,10488,4012],{"class":3718},[3417,10490,4015],{"class":3434},[3417,10492,3445],{"class":3430},[3417,10494,10496],{"class":3419,"line":10495},81,[3417,10497,3452],{"emptyLinePlaceholder":3451},[3417,10499,10501],{"class":3419,"line":10500},82,[3417,10502,10503],{"class":3423},"# Метаінформація дескриптора\n",[3417,10505,10507,10509,10511,10513,10515,10517,10520,10522,10525,10527,10529],{"class":3419,"line":10506},83,[3417,10508,4170],{"class":3734},[3417,10510,3738],{"class":3430},[3417,10512,4001],{"class":3718},[3417,10514,4015],{"class":3434},[3417,10516,6476],{"class":6475},[3417,10518,10519],{"class":3434},"Опис health: ",[3417,10521,4007],{"class":3718},[3417,10523,10524],{"class":3430},"Character.health",[3417,10526,4012],{"class":3718},[3417,10528,4015],{"class":3434},[3417,10530,3445],{"class":3430},[3417,10532,10534],{"class":3419,"line":10533},84,[3417,10535,3452],{"emptyLinePlaceholder":3451},[3417,10537,10539],{"class":3419,"line":10538},85,[3417,10540,10541],{"class":3423},"# Помилки\n",[3417,10543,10545],{"class":3419,"line":10544},86,[3417,10546,10547],{"class":3430},"tests = [\n",[3417,10549,10551,10554,10557,10559,10562,10565,10568,10570,10573,10576,10579],{"class":3419,"line":10550},87,[3417,10552,10553],{"class":3430},"    (",[3417,10555,10556],{"class":3718},"lambda",[3417,10558,3750],{"class":3430},[3417,10560,10561],{"class":3734},"setattr",[3417,10563,10564],{"class":3430},"(hero, ",[3417,10566,10567],{"class":3434},"'health'",[3417,10569,3438],{"class":3430},[3417,10571,10572],{"class":3441},"150",[3417,10574,10575],{"class":3430},"),  ",[3417,10577,10578],{"class":3434},"\"health=150 (>100)\"",[3417,10580,10581],{"class":3430},"),\n",[3417,10583,10585,10587,10589,10591,10593,10595,10598,10601,10603,10606,10609],{"class":3419,"line":10584},88,[3417,10586,10553],{"class":3430},[3417,10588,10556],{"class":3718},[3417,10590,3750],{"class":3430},[3417,10592,10561],{"class":3734},[3417,10594,10564],{"class":3430},[3417,10596,10597],{"class":3434},"'mana'",[3417,10599,10600],{"class":3430},", -",[3417,10602,10099],{"class":3441},[3417,10604,10605],{"class":3430},"),     ",[3417,10607,10608],{"class":3434},"\"mana=-1 (\u003C0)\"",[3417,10610,10581],{"class":3430},[3417,10612,10614,10616,10618,10620,10622,10624,10627,10629,10632,10635,10638],{"class":3419,"line":10613},89,[3417,10615,10553],{"class":3430},[3417,10617,10556],{"class":3718},[3417,10619,3750],{"class":3430},[3417,10621,10561],{"class":3734},[3417,10623,10564],{"class":3430},[3417,10625,10626],{"class":3434},"'level'",[3417,10628,3438],{"class":3430},[3417,10630,10631],{"class":3434},"\"max\"",[3417,10633,10634],{"class":3430},"), ",[3417,10636,10637],{"class":3434},"\"level='max' (не int)\"",[3417,10639,10581],{"class":3430},[3417,10641,10643],{"class":3419,"line":10642},90,[3417,10644,10645],{"class":3430},"]\n",[3417,10647,10649],{"class":3419,"line":10648},91,[3417,10650,3452],{"emptyLinePlaceholder":3451},[3417,10652,10654,10656,10659,10661],{"class":3419,"line":10653},92,[3417,10655,10437],{"class":3960},[3417,10657,10658],{"class":3430}," fn, label ",[3417,10660,10443],{"class":3960},[3417,10662,10663],{"class":3430}," tests:\n",[3417,10665,10667,10670],{"class":3419,"line":10666},93,[3417,10668,10669],{"class":3960},"    try",[3417,10671,3726],{"class":3430},[3417,10673,10675],{"class":3419,"line":10674},94,[3417,10676,10677],{"class":3430},"        fn()\n",[3417,10679,10681,10684,10686,10689,10691,10694,10697,10700],{"class":3419,"line":10680},95,[3417,10682,10683],{"class":3960},"    except",[3417,10685,6164],{"class":3430},[3417,10687,10688],{"class":3722},"ValueError",[3417,10690,3438],{"class":3430},[3417,10692,10693],{"class":3722},"TypeError",[3417,10695,10696],{"class":3430},") ",[3417,10698,10699],{"class":3960},"as",[3417,10701,4497],{"class":3430},[3417,10703,10705,10707,10709,10711,10714,10716,10719,10721,10724,10726,10728,10731,10733,10735,10737,10739,10741,10743,10745],{"class":3419,"line":10704},96,[3417,10706,4734],{"class":3734},[3417,10708,3738],{"class":3430},[3417,10710,4001],{"class":3718},[3417,10712,10713],{"class":3434},"\"[",[3417,10715,4007],{"class":3718},[3417,10717,10718],{"class":3430},"label",[3417,10720,4012],{"class":3718},[3417,10722,10723],{"class":3434},"] ",[3417,10725,4007],{"class":3718},[3417,10727,6056],{"class":3722},[3417,10729,10730],{"class":3430},"(e).",[3417,10732,6062],{"class":3741},[3417,10734,4012],{"class":3718},[3417,10736,3750],{"class":3434},[3417,10738,4007],{"class":3718},[3417,10740,4513],{"class":3430},[3417,10742,4012],{"class":3718},[3417,10744,4015],{"class":3434},[3417,10746,3445],{"class":3430},[4189,10748,10750,10758,10776,10784,10787,10798,10806,10809,10816,10819,10827,10835],{"title":10749},"python descriptors_validation.py",[4193,10751,10753,4201,10756],{"className":10752},[3419],[3417,10754,4200],{"className":10755},[4199],[3516,10757,10749],{},[4193,10759,10761,10762,10765,10766,10293,10769,10303,10772,10775],{"className":10760},[3419],"Character('Арагорн', HP=",[3417,10763,9041],{"className":10764},[4607],", MP=",[3417,10767,4709],{"className":10768},[4607],[3417,10770,6177],{"className":10771},[4607],[3417,10773,3967],{"className":10774},[4607],")",[4193,10777,10779,10780],{"className":10778},[3419],"HP після удару: ",[3417,10781,10783],{"className":10782},[4212],"75",[4193,10785],{"className":10786},[3419],[4193,10788,10429,10790,4625,10794],{"className":10789},[3419],[3417,10791,10793],{"className":10792},[4624],"['health', 'mana', 'level', 'armor']",[3417,10795,10797],{"className":10796},[4218],"# дескриптори тут!",[4193,10799,10801,10802],{"className":10800},[3419],"hero.__dict__: ",[3417,10803,10805],{"className":10804},[4624],"{'name': 'Арагорн', '_health': 75, '_mana': 30, '_level': 5, '_armor': 0}",[4193,10807],{"className":10808},[3419],[4193,10810,10519,10812],{"className":10811},[3419],[3417,10813,10815],{"className":10814},[4218],"IntegerRange(0, 100) → 'health'",[4193,10817],{"className":10818},[3419],[4193,10820,10822,10823],{"className":10821},[3419],"[health=150 (>100)] ",[3417,10824,10826],{"className":10825},[4598],"ValueError: 'health' має бути у [0, 100], отримано 150",[4193,10828,10830,10831],{"className":10829},[3419],"[mana=-1 (\u003C0)]     ",[3417,10832,10834],{"className":10833},[4598],"ValueError: 'mana' має бути у [0, 50], отримано -1",[4193,10836,10838,10839],{"className":10837},[3419],"[level='max']       ",[3417,10840,10842],{"className":10841},[4598],"TypeError: 'level' має бути int, отримано 'str'",[4952,10844,10845,10846,10849,10850,6164,10852,3438,10855,10858,10859,3438,10861,3438,10863,3438,10865,10867,10868,10871,10872,10875],{},"Зверніть увагу на ",[3399,10847,10848],{},"hero.__dict__",": значення зберігаються з префіксом ",[3399,10851,4258],{},[3399,10853,10854],{},"_health",[3399,10856,10857],{},"_mana","), а не під іменем дескриптора. Самі дескриптори (",[3399,10860,10146],{},[3399,10862,10155],{},[3399,10864,10164],{},[3399,10866,10173],{},") зберігаються у ",[3399,10869,10870],{},"Character.__dict__"," — на рівні ",[3516,10873,10874],{},"класу",", а не екземпляра.",[3553,10877],{},[3390,10879,10881],{"id":10880},"частина-vi-практичні-завдання","Частина VI: Практичні завдання",[3560,10883,10885],{"id":10884},"рівень-1-базовий-прямокутник-з-властивостями","Рівень 1 (Базовий): Прямокутник з властивостями",[3395,10887,10888,10889,3438,10891,10893],{},"Закріпіть синтаксис ",[3399,10890,5086],{},[3399,10892,5840],{}," та захищених атрибутів.",[3408,10895,10897],{"className":3410,"code":10896,"language":3412,"meta":3413,"style":3413},"# rectangle.py\nclass Rectangle:\n    \"\"\"\n    Прямокутник з валідацією розмірів через @property.\n    Площа та периметр — обчислювальні властивості (read-only).\n    \"\"\"\n\n    def __init__(self, width: float, height: float):\n        # Ці виклики проходять через сеттери — валідація відбувається вже тут\n        self.width  = width\n        self.height = height\n\n    @property\n    def width(self) -> float:\n        return self._width\n\n    @width.setter\n    def width(self, value: float) -> None:\n        if not isinstance(value, (int, float)):\n            raise TypeError(f\"width: очікується число, отримано {type(value).__name__}\")\n        if value \u003C= 0:\n            raise ValueError(f\"width має бути > 0, отримано {value}\")\n        self._width = float(value)\n\n    @property\n    def height(self) -> float:\n        return self._height\n\n    @height.setter\n    def height(self, value: float) -> None:\n        if not isinstance(value, (int, float)):\n            raise TypeError(f\"height: очікується число, отримано {type(value).__name__}\")\n        if value \u003C= 0:\n            raise ValueError(f\"height має бути > 0, отримано {value}\")\n        self._height = float(value)\n\n    @property\n    def area(self) -> float:\n        \"\"\"Площа (read-only, обчислюється).\"\"\"\n        return self._width * self._height\n\n    @property\n    def perimeter(self) -> float:\n        \"\"\"Периметр (read-only, обчислюється).\"\"\"\n        return 2 * (self._width + self._height)\n\n    @property\n    def is_square(self) -> bool:\n        \"\"\"Чи є прямокутник квадратом.\"\"\"\n        return self._width == self._height\n\n    def __repr__(self) -> str:\n        return f\"Rectangle(width={self._width}, height={self._height})\"\n\n\n# Тести\nr = Rectangle(4, 6)\nprint(r)\nprint(f\"Площа:    {r.area}\")\nprint(f\"Периметр: {r.perimeter}\")\nprint(f\"Квадрат:  {r.is_square}\")\n\nr.width = 6.0\nprint(f\"\\nПісля width=6: {r}\")\nprint(f\"Квадрат тепер: {r.is_square}\")\n\nfor bad_value, label in [(-1, \"від'ємна\"), (0, \"нуль\"), (\"широко\", \"рядок\")]:\n    try:\n        r.width = bad_value\n    except (ValueError, TypeError) as e:\n        print(f\"[{label}] {type(e).__name__}: {e}\")\n",[3399,10898,10899,10904,10913,10917,10922,10927,10931,10935,10965,10970,10977,10984,10988,10994,11011,11020,11024,11029,11053,11071,11098,11109,11132,11143,11147,11153,11170,11179,11183,11188,11212,11230,11257,11267,11290,11301,11305,11311,11327,11332,11345,11349,11355,11372,11377,11396,11400,11406,11424,11429,11442,11446,11462,11491,11495,11499,11504,11519,11526,11547,11568,11590,11594,11602,11628,11649,11653,11695,11701,11706,11724],{"__ignoreMap":3413},[3417,10900,10901],{"class":3419,"line":3420},[3417,10902,10903],{"class":3423},"# rectangle.py\n",[3417,10905,10906,10908,10911],{"class":3419,"line":3427},[3417,10907,3719],{"class":3718},[3417,10909,10910],{"class":3722}," Rectangle",[3417,10912,3726],{"class":3430},[3417,10914,10915],{"class":3419,"line":3448},[3417,10916,5877],{"class":3434},[3417,10918,10919],{"class":3419,"line":3455},[3417,10920,10921],{"class":3434},"    Прямокутник з валідацією розмірів через @property.\n",[3417,10923,10924],{"class":3419,"line":3461},[3417,10925,10926],{"class":3434},"    Площа та периметр — обчислювальні властивості (read-only).\n",[3417,10928,10929],{"class":3419,"line":3473},[3417,10930,5877],{"class":3434},[3417,10932,10933],{"class":3419,"line":3485},[3417,10934,3452],{"emptyLinePlaceholder":3451},[3417,10936,10937,10939,10941,10943,10945,10947,10950,10952,10954,10956,10959,10961,10963],{"class":3419,"line":3490},[3417,10938,3731],{"class":3718},[3417,10940,3735],{"class":3734},[3417,10942,3738],{"class":3430},[3417,10944,3742],{"class":3741},[3417,10946,3438],{"class":3430},[3417,10948,10949],{"class":3741},"width",[3417,10951,3750],{"class":3430},[3417,10953,3536],{"class":3722},[3417,10955,3438],{"class":3430},[3417,10957,10958],{"class":3741},"height",[3417,10960,3750],{"class":3430},[3417,10962,3536],{"class":3722},[3417,10964,3764],{"class":3430},[3417,10966,10967],{"class":3419,"line":3496},[3417,10968,10969],{"class":3423},"        # Ці виклики проходять через сеттери — валідація відбувається вже тут\n",[3417,10971,10972,10974],{"class":3419,"line":3645},[3417,10973,3769],{"class":3718},[3417,10975,10976],{"class":3430},".width  = width\n",[3417,10978,10979,10981],{"class":3419,"line":3651},[3417,10980,3769],{"class":3718},[3417,10982,10983],{"class":3430},".height = height\n",[3417,10985,10986],{"class":3419,"line":3657},[3417,10987,3452],{"emptyLinePlaceholder":3451},[3417,10989,10990,10992],{"class":3419,"line":3662},[3417,10991,5452],{"class":3734},[3417,10993,5455],{"class":3722},[3417,10995,10996,10998,11001,11003,11005,11007,11009],{"class":3419,"line":3668},[3417,10997,3731],{"class":3718},[3417,10999,11000],{"class":3734}," width",[3417,11002,3738],{"class":3430},[3417,11004,3742],{"class":3741},[3417,11006,3950],{"class":3430},[3417,11008,3536],{"class":3722},[3417,11010,3726],{"class":3430},[3417,11012,11013,11015,11017],{"class":3419,"line":3673},[3417,11014,4130],{"class":3960},[3417,11016,4133],{"class":3718},[3417,11018,11019],{"class":3430},"._width\n",[3417,11021,11022],{"class":3419,"line":3679},[3417,11023,3452],{"emptyLinePlaceholder":3451},[3417,11025,11026],{"class":3419,"line":3685},[3417,11027,11028],{"class":3734},"    @width.setter\n",[3417,11030,11031,11033,11035,11037,11039,11041,11043,11045,11047,11049,11051],{"class":3419,"line":4074},[3417,11032,3731],{"class":3718},[3417,11034,11000],{"class":3734},[3417,11036,3738],{"class":3430},[3417,11038,3742],{"class":3741},[3417,11040,3438],{"class":3430},[3417,11042,5200],{"class":3741},[3417,11044,3750],{"class":3430},[3417,11046,3536],{"class":3722},[3417,11048,3950],{"class":3430},[3417,11050,3953],{"class":3718},[3417,11052,3726],{"class":3430},[3417,11054,11055,11057,11059,11061,11063,11065,11067,11069],{"class":3419,"line":4082},[3417,11056,3961],{"class":3960},[3417,11058,6021],{"class":3718},[3417,11060,6024],{"class":3734},[3417,11062,6027],{"class":3430},[3417,11064,6030],{"class":3722},[3417,11066,3438],{"class":3430},[3417,11068,3536],{"class":3722},[3417,11070,6037],{"class":3430},[3417,11072,11073,11075,11077,11079,11081,11084,11086,11088,11090,11092,11094,11096],{"class":3419,"line":4104},[3417,11074,3974],{"class":3960},[3417,11076,6044],{"class":3722},[3417,11078,3738],{"class":3430},[3417,11080,4001],{"class":3718},[3417,11082,11083],{"class":3434},"\"width: очікується число, отримано ",[3417,11085,4007],{"class":3718},[3417,11087,6056],{"class":3722},[3417,11089,6059],{"class":3430},[3417,11091,6062],{"class":3741},[3417,11093,4012],{"class":3718},[3417,11095,4015],{"class":3434},[3417,11097,3445],{"class":3430},[3417,11099,11100,11102,11105,11107],{"class":3419,"line":4109},[3417,11101,3961],{"class":3960},[3417,11103,11104],{"class":3430}," value \u003C= ",[3417,11106,3967],{"class":3441},[3417,11108,3726],{"class":3430},[3417,11110,11111,11113,11115,11117,11119,11122,11124,11126,11128,11130],{"class":3419,"line":4127},[3417,11112,3974],{"class":3960},[3417,11114,3977],{"class":3722},[3417,11116,3738],{"class":3430},[3417,11118,4001],{"class":3718},[3417,11120,11121],{"class":3434},"\"width має бути > 0, отримано ",[3417,11123,4007],{"class":3718},[3417,11125,5200],{"class":3430},[3417,11127,4012],{"class":3718},[3417,11129,4015],{"class":3434},[3417,11131,3445],{"class":3430},[3417,11133,11134,11136,11139,11141],{"class":3419,"line":4522},[3417,11135,3769],{"class":3718},[3417,11137,11138],{"class":3430},"._width = ",[3417,11140,3536],{"class":3722},[3417,11142,6116],{"class":3430},[3417,11144,11145],{"class":3419,"line":4527},[3417,11146,3452],{"emptyLinePlaceholder":3451},[3417,11148,11149,11151],{"class":3419,"line":4533},[3417,11150,5452],{"class":3734},[3417,11152,5455],{"class":3722},[3417,11154,11155,11157,11160,11162,11164,11166,11168],{"class":3419,"line":4546},[3417,11156,3731],{"class":3718},[3417,11158,11159],{"class":3734}," height",[3417,11161,3738],{"class":3430},[3417,11163,3742],{"class":3741},[3417,11165,3950],{"class":3430},[3417,11167,3536],{"class":3722},[3417,11169,3726],{"class":3430},[3417,11171,11172,11174,11176],{"class":3419,"line":4551},[3417,11173,4130],{"class":3960},[3417,11175,4133],{"class":3718},[3417,11177,11178],{"class":3430},"._height\n",[3417,11180,11181],{"class":3419,"line":4557},[3417,11182,3452],{"emptyLinePlaceholder":3451},[3417,11184,11185],{"class":3419,"line":4565},[3417,11186,11187],{"class":3734},"    @height.setter\n",[3417,11189,11190,11192,11194,11196,11198,11200,11202,11204,11206,11208,11210],{"class":3419,"line":4574},[3417,11191,3731],{"class":3718},[3417,11193,11159],{"class":3734},[3417,11195,3738],{"class":3430},[3417,11197,3742],{"class":3741},[3417,11199,3438],{"class":3430},[3417,11201,5200],{"class":3741},[3417,11203,3750],{"class":3430},[3417,11205,3536],{"class":3722},[3417,11207,3950],{"class":3430},[3417,11209,3953],{"class":3718},[3417,11211,3726],{"class":3430},[3417,11213,11214,11216,11218,11220,11222,11224,11226,11228],{"class":3419,"line":5657},[3417,11215,3961],{"class":3960},[3417,11217,6021],{"class":3718},[3417,11219,6024],{"class":3734},[3417,11221,6027],{"class":3430},[3417,11223,6030],{"class":3722},[3417,11225,3438],{"class":3430},[3417,11227,3536],{"class":3722},[3417,11229,6037],{"class":3430},[3417,11231,11232,11234,11236,11238,11240,11243,11245,11247,11249,11251,11253,11255],{"class":3419,"line":5680},[3417,11233,3974],{"class":3960},[3417,11235,6044],{"class":3722},[3417,11237,3738],{"class":3430},[3417,11239,4001],{"class":3718},[3417,11241,11242],{"class":3434},"\"height: очікується число, отримано ",[3417,11244,4007],{"class":3718},[3417,11246,6056],{"class":3722},[3417,11248,6059],{"class":3430},[3417,11250,6062],{"class":3741},[3417,11252,4012],{"class":3718},[3417,11254,4015],{"class":3434},[3417,11256,3445],{"class":3430},[3417,11258,11259,11261,11263,11265],{"class":3419,"line":5685},[3417,11260,3961],{"class":3960},[3417,11262,11104],{"class":3430},[3417,11264,3967],{"class":3441},[3417,11266,3726],{"class":3430},[3417,11268,11269,11271,11273,11275,11277,11280,11282,11284,11286,11288],{"class":3419,"line":5691},[3417,11270,3974],{"class":3960},[3417,11272,3977],{"class":3722},[3417,11274,3738],{"class":3430},[3417,11276,4001],{"class":3718},[3417,11278,11279],{"class":3434},"\"height має бути > 0, отримано ",[3417,11281,4007],{"class":3718},[3417,11283,5200],{"class":3430},[3417,11285,4012],{"class":3718},[3417,11287,4015],{"class":3434},[3417,11289,3445],{"class":3430},[3417,11291,11292,11294,11297,11299],{"class":3419,"line":5700},[3417,11293,3769],{"class":3718},[3417,11295,11296],{"class":3430},"._height = ",[3417,11298,3536],{"class":3722},[3417,11300,6116],{"class":3430},[3417,11302,11303],{"class":3419,"line":5722},[3417,11304,3452],{"emptyLinePlaceholder":3451},[3417,11306,11307,11309],{"class":3419,"line":5727},[3417,11308,5452],{"class":3734},[3417,11310,5455],{"class":3722},[3417,11312,11313,11315,11317,11319,11321,11323,11325],{"class":3419,"line":5733},[3417,11314,3731],{"class":3718},[3417,11316,5462],{"class":3734},[3417,11318,3738],{"class":3430},[3417,11320,3742],{"class":3741},[3417,11322,3950],{"class":3430},[3417,11324,3536],{"class":3722},[3417,11326,3726],{"class":3430},[3417,11328,11329],{"class":3419,"line":5740},[3417,11330,11331],{"class":3434},"        \"\"\"Площа (read-only, обчислюється).\"\"\"\n",[3417,11333,11334,11336,11338,11341,11343],{"class":3419,"line":5749},[3417,11335,4130],{"class":3960},[3417,11337,4133],{"class":3718},[3417,11339,11340],{"class":3430},"._width * ",[3417,11342,3742],{"class":3718},[3417,11344,11178],{"class":3430},[3417,11346,11347],{"class":3419,"line":5760},[3417,11348,3452],{"emptyLinePlaceholder":3451},[3417,11350,11351,11353],{"class":3419,"line":6271},[3417,11352,5452],{"class":3734},[3417,11354,5455],{"class":3722},[3417,11356,11357,11359,11362,11364,11366,11368,11370],{"class":3419,"line":6277},[3417,11358,3731],{"class":3718},[3417,11360,11361],{"class":3734}," perimeter",[3417,11363,3738],{"class":3430},[3417,11365,3742],{"class":3741},[3417,11367,3950],{"class":3430},[3417,11369,3536],{"class":3722},[3417,11371,3726],{"class":3430},[3417,11373,11374],{"class":3419,"line":6289},[3417,11375,11376],{"class":3434},"        \"\"\"Периметр (read-only, обчислюється).\"\"\"\n",[3417,11378,11379,11381,11383,11386,11388,11391,11393],{"class":3419,"line":6299},[3417,11380,4130],{"class":3960},[3417,11382,5531],{"class":3441},[3417,11384,11385],{"class":3430}," * (",[3417,11387,3742],{"class":3718},[3417,11389,11390],{"class":3430},"._width + ",[3417,11392,3742],{"class":3718},[3417,11394,11395],{"class":3430},"._height)\n",[3417,11397,11398],{"class":3419,"line":6304},[3417,11399,3452],{"emptyLinePlaceholder":3451},[3417,11401,11402,11404],{"class":3419,"line":6310},[3417,11403,5452],{"class":3734},[3417,11405,5455],{"class":3722},[3417,11407,11408,11410,11413,11415,11417,11419,11422],{"class":3419,"line":6315},[3417,11409,3731],{"class":3718},[3417,11411,11412],{"class":3734}," is_square",[3417,11414,3738],{"class":3430},[3417,11416,3742],{"class":3741},[3417,11418,3950],{"class":3430},[3417,11420,11421],{"class":3722},"bool",[3417,11423,3726],{"class":3430},[3417,11425,11426],{"class":3419,"line":6322},[3417,11427,11428],{"class":3434},"        \"\"\"Чи є прямокутник квадратом.\"\"\"\n",[3417,11430,11431,11433,11435,11438,11440],{"class":3419,"line":6340},[3417,11432,4130],{"class":3960},[3417,11434,4133],{"class":3718},[3417,11436,11437],{"class":3430},"._width == ",[3417,11439,3742],{"class":3718},[3417,11441,11178],{"class":3430},[3417,11443,11444],{"class":3419,"line":6346},[3417,11445,3452],{"emptyLinePlaceholder":3451},[3417,11447,11448,11450,11452,11454,11456,11458,11460],{"class":3419,"line":8484},[3417,11449,3731],{"class":3718},[3417,11451,9979],{"class":3734},[3417,11453,3738],{"class":3430},[3417,11455,3742],{"class":3741},[3417,11457,3950],{"class":3430},[3417,11459,3876],{"class":3722},[3417,11461,3726],{"class":3430},[3417,11463,11464,11466,11469,11472,11474,11477,11479,11482,11484,11487,11489],{"class":3419,"line":8509},[3417,11465,4130],{"class":3960},[3417,11467,11468],{"class":3718}," f",[3417,11470,11471],{"class":3434},"\"Rectangle(width=",[3417,11473,4744],{"class":3718},[3417,11475,11476],{"class":3430},"._width",[3417,11478,4012],{"class":3718},[3417,11480,11481],{"class":3434},", height=",[3417,11483,4744],{"class":3718},[3417,11485,11486],{"class":3430},"._height",[3417,11488,4012],{"class":3718},[3417,11490,10313],{"class":3434},[3417,11492,11493],{"class":3419,"line":8520},[3417,11494,3452],{"emptyLinePlaceholder":3451},[3417,11496,11497],{"class":3419,"line":8534},[3417,11498,3452],{"emptyLinePlaceholder":3451},[3417,11500,11501],{"class":3419,"line":8542},[3417,11502,11503],{"class":3423},"# Тести\n",[3417,11505,11506,11509,11512,11514,11517],{"class":3419,"line":8547},[3417,11507,11508],{"class":3430},"r = Rectangle(",[3417,11510,11511],{"class":3441},"4",[3417,11513,3438],{"class":3430},[3417,11515,11516],{"class":3441},"6",[3417,11518,3445],{"class":3430},[3417,11520,11521,11523],{"class":3419,"line":8552},[3417,11522,4170],{"class":3734},[3417,11524,11525],{"class":3430},"(r)\n",[3417,11527,11528,11530,11532,11534,11536,11538,11541,11543,11545],{"class":3419,"line":8563},[3417,11529,4170],{"class":3734},[3417,11531,3738],{"class":3430},[3417,11533,4001],{"class":3718},[3417,11535,5620],{"class":3434},[3417,11537,4007],{"class":3718},[3417,11539,11540],{"class":3430},"r.area",[3417,11542,4012],{"class":3718},[3417,11544,4015],{"class":3434},[3417,11546,3445],{"class":3430},[3417,11548,11549,11551,11553,11555,11557,11559,11562,11564,11566],{"class":3419,"line":8574},[3417,11550,4170],{"class":3734},[3417,11552,3738],{"class":3430},[3417,11554,4001],{"class":3718},[3417,11556,5643],{"class":3434},[3417,11558,4007],{"class":3718},[3417,11560,11561],{"class":3430},"r.perimeter",[3417,11563,4012],{"class":3718},[3417,11565,4015],{"class":3434},[3417,11567,3445],{"class":3430},[3417,11569,11570,11572,11574,11576,11579,11581,11584,11586,11588],{"class":3419,"line":8583},[3417,11571,4170],{"class":3734},[3417,11573,3738],{"class":3430},[3417,11575,4001],{"class":3718},[3417,11577,11578],{"class":3434},"\"Квадрат:  ",[3417,11580,4007],{"class":3718},[3417,11582,11583],{"class":3430},"r.is_square",[3417,11585,4012],{"class":3718},[3417,11587,4015],{"class":3434},[3417,11589,3445],{"class":3430},[3417,11591,11592],{"class":3419,"line":8593},[3417,11593,3452],{"emptyLinePlaceholder":3451},[3417,11595,11596,11599],{"class":3419,"line":8598},[3417,11597,11598],{"class":3430},"r.width = ",[3417,11600,11601],{"class":3441},"6.0\n",[3417,11603,11604,11606,11608,11610,11612,11614,11617,11619,11622,11624,11626],{"class":3419,"line":8605},[3417,11605,4170],{"class":3734},[3417,11607,3738],{"class":3430},[3417,11609,4001],{"class":3718},[3417,11611,4015],{"class":3434},[3417,11613,6476],{"class":6475},[3417,11615,11616],{"class":3434},"Після width=6: ",[3417,11618,4007],{"class":3718},[3417,11620,11621],{"class":3430},"r",[3417,11623,4012],{"class":3718},[3417,11625,4015],{"class":3434},[3417,11627,3445],{"class":3430},[3417,11629,11630,11632,11634,11636,11639,11641,11643,11645,11647],{"class":3419,"line":8613},[3417,11631,4170],{"class":3734},[3417,11633,3738],{"class":3430},[3417,11635,4001],{"class":3718},[3417,11637,11638],{"class":3434},"\"Квадрат тепер: ",[3417,11640,4007],{"class":3718},[3417,11642,11583],{"class":3430},[3417,11644,4012],{"class":3718},[3417,11646,4015],{"class":3434},[3417,11648,3445],{"class":3430},[3417,11650,11651],{"class":3419,"line":8624},[3417,11652,3452],{"emptyLinePlaceholder":3451},[3417,11654,11655,11657,11660,11662,11665,11667,11669,11672,11675,11677,11679,11682,11684,11687,11689,11692],{"class":3419,"line":10278},[3417,11656,10437],{"class":3960},[3417,11658,11659],{"class":3430}," bad_value, label ",[3417,11661,10443],{"class":3960},[3417,11663,11664],{"class":3430}," [(-",[3417,11666,10099],{"class":3441},[3417,11668,3438],{"class":3430},[3417,11670,11671],{"class":3434},"\"від'ємна\"",[3417,11673,11674],{"class":3430},"), (",[3417,11676,3967],{"class":3441},[3417,11678,3438],{"class":3430},[3417,11680,11681],{"class":3434},"\"нуль\"",[3417,11683,11674],{"class":3430},[3417,11685,11686],{"class":3434},"\"широко\"",[3417,11688,3438],{"class":3430},[3417,11690,11691],{"class":3434},"\"рядок\"",[3417,11693,11694],{"class":3430},")]:\n",[3417,11696,11697,11699],{"class":3419,"line":10316},[3417,11698,10669],{"class":3960},[3417,11700,3726],{"class":3430},[3417,11702,11703],{"class":3419,"line":10321},[3417,11704,11705],{"class":3430},"        r.width = bad_value\n",[3417,11707,11708,11710,11712,11714,11716,11718,11720,11722],{"class":3419,"line":10326},[3417,11709,10683],{"class":3960},[3417,11711,6164],{"class":3430},[3417,11713,10688],{"class":3722},[3417,11715,3438],{"class":3430},[3417,11717,10693],{"class":3722},[3417,11719,10696],{"class":3430},[3417,11721,10699],{"class":3960},[3417,11723,4497],{"class":3430},[3417,11725,11726,11728,11730,11732,11734,11736,11738,11740,11742,11744,11746,11748,11750,11752,11754,11756,11758,11760,11762],{"class":3419,"line":10331},[3417,11727,4734],{"class":3734},[3417,11729,3738],{"class":3430},[3417,11731,4001],{"class":3718},[3417,11733,10713],{"class":3434},[3417,11735,4007],{"class":3718},[3417,11737,10718],{"class":3430},[3417,11739,4012],{"class":3718},[3417,11741,10723],{"class":3434},[3417,11743,4007],{"class":3718},[3417,11745,6056],{"class":3722},[3417,11747,10730],{"class":3430},[3417,11749,6062],{"class":3741},[3417,11751,4012],{"class":3718},[3417,11753,3750],{"class":3434},[3417,11755,4007],{"class":3718},[3417,11757,4513],{"class":3430},[3417,11759,4012],{"class":3718},[3417,11761,4015],{"class":3434},[3417,11763,3445],{"class":3430},[4189,11765,11767,11775,11786,11793,11800,11808,11811,11821,11829,11832,11840,11848],{"title":11766},"python rectangle.py",[4193,11768,11770,4201,11773],{"className":11769},[3419],[3417,11771,4200],{"className":11772},[4199],[3516,11774,11766],{},[4193,11776,11778,11779,11481,11782,10775],{"className":11777},[3419],"Rectangle(width=",[3417,11780,3802],{"className":11781},[4607],[3417,11783,11785],{"className":11784},[4607],"6.0",[4193,11787,5793,11789],{"className":11788},[3419],[3417,11790,11792],{"className":11791},[4607],"24.0",[4193,11794,5801,11796],{"className":11795},[3419],[3417,11797,11799],{"className":11798},[4607],"20.0",[4193,11801,11803,11804],{"className":11802},[3419],"Квадрат:  ",[3417,11805,11807],{"className":11806},[4598],"False",[4193,11809],{"className":11810},[3419],[4193,11812,11814,11815,11481,11818,10775],{"className":11813},[3419],"Після width=6: Rectangle(width=",[3417,11816,11785],{"className":11817},[4607],[3417,11819,11785],{"className":11820},[4607],[4193,11822,11824,11825],{"className":11823},[3419],"Квадрат тепер: ",[3417,11826,11828],{"className":11827},[4607],"True",[4193,11830],{"className":11831},[3419],[4193,11833,11835,11836],{"className":11834},[3419],"[від'ємна] ",[3417,11837,11839],{"className":11838},[4598],"ValueError: width має бути > 0, отримано -1",[4193,11841,11843,11844],{"className":11842},[3419],"[нуль]     ",[3417,11845,11847],{"className":11846},[4598],"ValueError: width має бути > 0, отримано 0",[4193,11849,11851,11852],{"className":11850},[3419],"[рядок]    ",[3417,11853,11855],{"className":11854},[4598],"TypeError: width: очікується число, отримано str",[3553,11857],{},[3560,11859,11861],{"id":11860},"рівень-2-середній-система-профілів-користувачів","Рівень 2 (Середній): Система профілів користувачів",[3395,11863,11864,11865,11867],{},"Production-сценарій: комбінація ",[3399,11866,5086],{}," та перевикористовуваних дескрипторів.",[11869,11870,11871,12949,13586],"code-tree",{},[3408,11872,11875],{"className":3410,"code":11873,"filename":11874,"language":3412,"meta":3413,"style":3413},"# Перевикористовувані дескриптори-валідатори\n\nclass NonEmptyString:\n    \"\"\"Дескриптор: непорожній рядок з автоматичним trim.\"\"\"\n\n    def __set_name__(self, owner: type, name: str) -> None:\n        self.public_name  = name\n        self.private_name = f\"_{name}\"\n\n    def __get__(self, instance, owner):\n        if instance is None:\n            return self\n        return getattr(instance, self.private_name, \"\")\n\n    def __set__(self, instance, value: str) -> None:\n        if not isinstance(value, str):\n            raise TypeError(\n                f\"'{self.public_name}' має бути str, \"\n                f\"отримано {type(value).__name__!r}\"\n            )\n        cleaned = value.strip()\n        if not cleaned:\n            raise ValueError(f\"'{self.public_name}' не може бути порожнім\")\n        setattr(instance, self.private_name, cleaned)\n\n\nclass EmailAttribute:\n    \"\"\"Дескриптор: валідний email-адрес (normalize to lowercase).\"\"\"\n\n    def __set_name__(self, owner: type, name: str) -> None:\n        self.public_name  = name\n        self.private_name = f\"_{name}\"\n\n    def __get__(self, instance, owner):\n        if instance is None:\n            return self\n        return getattr(instance, self.private_name, \"\")\n\n    def __set__(self, instance, value: str) -> None:\n        if not isinstance(value, str):\n            raise TypeError(f\"'{self.public_name}' має бути str\")\n        normalized = value.strip().lower()\n        if normalized.count(\"@\") != 1:\n            raise ValueError(\n                f\"'{self.public_name}': email має містити рівно один '@'\"\n            )\n        local, domain = normalized.split(\"@\")\n        if not local:\n            raise ValueError(f\"'{self.public_name}': локальна частина порожня\")\n        if \".\" not in domain or domain.startswith(\".\") or domain.endswith(\".\"):\n            raise ValueError(\n                f\"'{self.public_name}': невалідна доменна частина '{domain}'\"\n            )\n        setattr(instance, self.private_name, normalized)\n\n\nclass PhoneNumber:\n    \"\"\"Дескриптор: номер телефону у форматі +XXXXXXXXXXX (10-15 цифр після +).\"\"\"\n\n    def __set_name__(self, owner: type, name: str) -> None:\n        self.public_name  = name\n        self.private_name = f\"_{name}\"\n\n    def __get__(self, instance, owner):\n        if instance is None:\n            return self\n        return getattr(instance, self.private_name, None)\n\n    def __set__(self, instance, value: str | None) -> None:\n        if value is None:\n            setattr(instance, self.private_name, None)\n            return\n        if not isinstance(value, str):\n            raise TypeError(f\"'{self.public_name}' має бути str або None\")\n        stripped = value.strip()\n        if not stripped.startswith(\"+\"):\n            raise ValueError(f\"'{self.public_name}': має починатися з '+'\")\n        digits = stripped[1:]\n        if not digits.isdigit():\n            raise ValueError(f\"'{self.public_name}': після '+' лише цифри\")\n        if not (10 \u003C= len(digits) \u003C= 15):\n            raise ValueError(\n                f\"'{self.public_name}': кількість цифр має бути 10–15, \"\n                f\"отримано {len(digits)}\"\n            )\n        setattr(instance, self.private_name, stripped)\n","validators.py",[3399,11876,11877,11882,11886,11895,11900,11904,11936,11942,11960,11964,11984,11996,12002,12019,12023,12051,12065,12073,12088,12106,12110,12115,12124,12147,12158,12162,12166,12175,12180,12184,12216,12222,12240,12244,12264,12276,12282,12298,12302,12330,12344,12367,12372,12389,12397,12412,12416,12425,12434,12457,12491,12499,12523,12527,12538,12542,12546,12555,12560,12564,12596,12602,12620,12624,12644,12656,12662,12678,12682,12715,12728,12743,12748,12762,12785,12790,12804,12827,12837,12846,12869,12894,12902,12917,12934,12938],{"__ignoreMap":3413},[3417,11878,11879],{"class":3419,"line":3420},[3417,11880,11881],{"class":3423},"# Перевикористовувані дескриптори-валідатори\n",[3417,11883,11884],{"class":3419,"line":3427},[3417,11885,3452],{"emptyLinePlaceholder":3451},[3417,11887,11888,11890,11893],{"class":3419,"line":3448},[3417,11889,3719],{"class":3718},[3417,11891,11892],{"class":3722}," NonEmptyString",[3417,11894,3726],{"class":3430},[3417,11896,11897],{"class":3419,"line":3455},[3417,11898,11899],{"class":3434},"    \"\"\"Дескриптор: непорожній рядок з автоматичним trim.\"\"\"\n",[3417,11901,11902],{"class":3419,"line":3461},[3417,11903,3452],{"emptyLinePlaceholder":3451},[3417,11905,11906,11908,11910,11912,11914,11916,11918,11920,11922,11924,11926,11928,11930,11932,11934],{"class":3419,"line":3473},[3417,11907,3731],{"class":3718},[3417,11909,9409],{"class":3734},[3417,11911,3738],{"class":3430},[3417,11913,3742],{"class":3741},[3417,11915,3438],{"class":3430},[3417,11917,3871],{"class":3741},[3417,11919,3750],{"class":3430},[3417,11921,6056],{"class":3722},[3417,11923,3438],{"class":3430},[3417,11925,9422],{"class":3741},[3417,11927,3750],{"class":3430},[3417,11929,3876],{"class":3722},[3417,11931,3950],{"class":3430},[3417,11933,3953],{"class":3718},[3417,11935,3726],{"class":3430},[3417,11937,11938,11940],{"class":3419,"line":3485},[3417,11939,3769],{"class":3718},[3417,11941,9677],{"class":3430},[3417,11943,11944,11946,11948,11950,11952,11954,11956,11958],{"class":3419,"line":3490},[3417,11945,3769],{"class":3718},[3417,11947,9451],{"class":3430},[3417,11949,4001],{"class":3718},[3417,11951,9456],{"class":3434},[3417,11953,4007],{"class":3718},[3417,11955,9422],{"class":3430},[3417,11957,4012],{"class":3718},[3417,11959,9696],{"class":3434},[3417,11961,11962],{"class":3419,"line":3496},[3417,11963,3452],{"emptyLinePlaceholder":3451},[3417,11965,11966,11968,11970,11972,11974,11976,11978,11980,11982],{"class":3419,"line":3645},[3417,11967,3731],{"class":3718},[3417,11969,8086],{"class":3734},[3417,11971,3738],{"class":3430},[3417,11973,3742],{"class":3741},[3417,11975,3438],{"class":3430},[3417,11977,7441],{"class":3741},[3417,11979,3438],{"class":3430},[3417,11981,3871],{"class":3741},[3417,11983,3764],{"class":3430},[3417,11985,11986,11988,11990,11992,11994],{"class":3419,"line":3651},[3417,11987,3961],{"class":3960},[3417,11989,8112],{"class":3430},[3417,11991,8115],{"class":3718},[3417,11993,8073],{"class":3718},[3417,11995,3726],{"class":3430},[3417,11997,11998,12000],{"class":3419,"line":3657},[3417,11999,8124],{"class":3960},[3417,12001,8127],{"class":3718},[3417,12003,12004,12006,12008,12010,12012,12014,12017],{"class":3419,"line":3662},[3417,12005,4130],{"class":3960},[3417,12007,9752],{"class":3734},[3417,12009,9755],{"class":3430},[3417,12011,3742],{"class":3718},[3417,12013,9760],{"class":3430},[3417,12015,12016],{"class":3434},"\"\"",[3417,12018,3445],{"class":3430},[3417,12020,12021],{"class":3419,"line":3668},[3417,12022,3452],{"emptyLinePlaceholder":3451},[3417,12024,12025,12027,12029,12031,12033,12035,12037,12039,12041,12043,12045,12047,12049],{"class":3419,"line":3673},[3417,12026,3731],{"class":3718},[3417,12028,8175],{"class":3734},[3417,12030,3738],{"class":3430},[3417,12032,3742],{"class":3741},[3417,12034,3438],{"class":3430},[3417,12036,7441],{"class":3741},[3417,12038,3438],{"class":3430},[3417,12040,5200],{"class":3741},[3417,12042,3750],{"class":3430},[3417,12044,3876],{"class":3722},[3417,12046,3950],{"class":3430},[3417,12048,3953],{"class":3718},[3417,12050,3726],{"class":3430},[3417,12052,12053,12055,12057,12059,12061,12063],{"class":3419,"line":3679},[3417,12054,3961],{"class":3960},[3417,12056,6021],{"class":3718},[3417,12058,6024],{"class":3734},[3417,12060,9812],{"class":3430},[3417,12062,3876],{"class":3722},[3417,12064,3764],{"class":3430},[3417,12066,12067,12069,12071],{"class":3419,"line":3685},[3417,12068,3974],{"class":3960},[3417,12070,6044],{"class":3722},[3417,12072,9825],{"class":3430},[3417,12074,12075,12077,12079,12081,12083,12085],{"class":3419,"line":4074},[3417,12076,9830],{"class":3718},[3417,12078,9833],{"class":3434},[3417,12080,4744],{"class":3718},[3417,12082,9838],{"class":3430},[3417,12084,4012],{"class":3718},[3417,12086,12087],{"class":3434},"' має бути str, \"\n",[3417,12089,12090,12092,12094,12096,12098,12100,12102,12104],{"class":3419,"line":4082},[3417,12091,9830],{"class":3718},[3417,12093,9850],{"class":3434},[3417,12095,4007],{"class":3718},[3417,12097,6056],{"class":3722},[3417,12099,6059],{"class":3430},[3417,12101,6062],{"class":3741},[3417,12103,9861],{"class":3718},[3417,12105,9696],{"class":3434},[3417,12107,12108],{"class":3419,"line":4104},[3417,12109,9868],{"class":3430},[3417,12111,12112],{"class":3419,"line":4109},[3417,12113,12114],{"class":3430},"        cleaned = value.strip()\n",[3417,12116,12117,12119,12121],{"class":3419,"line":4127},[3417,12118,3961],{"class":3960},[3417,12120,6021],{"class":3718},[3417,12122,12123],{"class":3430}," cleaned:\n",[3417,12125,12126,12128,12130,12132,12134,12136,12138,12140,12142,12145],{"class":3419,"line":4522},[3417,12127,3974],{"class":3960},[3417,12129,3977],{"class":3722},[3417,12131,3738],{"class":3430},[3417,12133,4001],{"class":3718},[3417,12135,9833],{"class":3434},[3417,12137,4744],{"class":3718},[3417,12139,9838],{"class":3430},[3417,12141,4012],{"class":3718},[3417,12143,12144],{"class":3434},"' не може бути порожнім\"",[3417,12146,3445],{"class":3430},[3417,12148,12149,12151,12153,12155],{"class":3419,"line":4527},[3417,12150,9961],{"class":3734},[3417,12152,9755],{"class":3430},[3417,12154,3742],{"class":3718},[3417,12156,12157],{"class":3430},".private_name, cleaned)\n",[3417,12159,12160],{"class":3419,"line":4533},[3417,12161,3452],{"emptyLinePlaceholder":3451},[3417,12163,12164],{"class":3419,"line":4546},[3417,12165,3452],{"emptyLinePlaceholder":3451},[3417,12167,12168,12170,12173],{"class":3419,"line":4551},[3417,12169,3719],{"class":3718},[3417,12171,12172],{"class":3722}," EmailAttribute",[3417,12174,3726],{"class":3430},[3417,12176,12177],{"class":3419,"line":4557},[3417,12178,12179],{"class":3434},"    \"\"\"Дескриптор: валідний email-адрес (normalize to lowercase).\"\"\"\n",[3417,12181,12182],{"class":3419,"line":4565},[3417,12183,3452],{"emptyLinePlaceholder":3451},[3417,12185,12186,12188,12190,12192,12194,12196,12198,12200,12202,12204,12206,12208,12210,12212,12214],{"class":3419,"line":4574},[3417,12187,3731],{"class":3718},[3417,12189,9409],{"class":3734},[3417,12191,3738],{"class":3430},[3417,12193,3742],{"class":3741},[3417,12195,3438],{"class":3430},[3417,12197,3871],{"class":3741},[3417,12199,3750],{"class":3430},[3417,12201,6056],{"class":3722},[3417,12203,3438],{"class":3430},[3417,12205,9422],{"class":3741},[3417,12207,3750],{"class":3430},[3417,12209,3876],{"class":3722},[3417,12211,3950],{"class":3430},[3417,12213,3953],{"class":3718},[3417,12215,3726],{"class":3430},[3417,12217,12218,12220],{"class":3419,"line":5657},[3417,12219,3769],{"class":3718},[3417,12221,9677],{"class":3430},[3417,12223,12224,12226,12228,12230,12232,12234,12236,12238],{"class":3419,"line":5680},[3417,12225,3769],{"class":3718},[3417,12227,9451],{"class":3430},[3417,12229,4001],{"class":3718},[3417,12231,9456],{"class":3434},[3417,12233,4007],{"class":3718},[3417,12235,9422],{"class":3430},[3417,12237,4012],{"class":3718},[3417,12239,9696],{"class":3434},[3417,12241,12242],{"class":3419,"line":5685},[3417,12243,3452],{"emptyLinePlaceholder":3451},[3417,12245,12246,12248,12250,12252,12254,12256,12258,12260,12262],{"class":3419,"line":5691},[3417,12247,3731],{"class":3718},[3417,12249,8086],{"class":3734},[3417,12251,3738],{"class":3430},[3417,12253,3742],{"class":3741},[3417,12255,3438],{"class":3430},[3417,12257,7441],{"class":3741},[3417,12259,3438],{"class":3430},[3417,12261,3871],{"class":3741},[3417,12263,3764],{"class":3430},[3417,12265,12266,12268,12270,12272,12274],{"class":3419,"line":5700},[3417,12267,3961],{"class":3960},[3417,12269,8112],{"class":3430},[3417,12271,8115],{"class":3718},[3417,12273,8073],{"class":3718},[3417,12275,3726],{"class":3430},[3417,12277,12278,12280],{"class":3419,"line":5722},[3417,12279,8124],{"class":3960},[3417,12281,8127],{"class":3718},[3417,12283,12284,12286,12288,12290,12292,12294,12296],{"class":3419,"line":5727},[3417,12285,4130],{"class":3960},[3417,12287,9752],{"class":3734},[3417,12289,9755],{"class":3430},[3417,12291,3742],{"class":3718},[3417,12293,9760],{"class":3430},[3417,12295,12016],{"class":3434},[3417,12297,3445],{"class":3430},[3417,12299,12300],{"class":3419,"line":5733},[3417,12301,3452],{"emptyLinePlaceholder":3451},[3417,12303,12304,12306,12308,12310,12312,12314,12316,12318,12320,12322,12324,12326,12328],{"class":3419,"line":5740},[3417,12305,3731],{"class":3718},[3417,12307,8175],{"class":3734},[3417,12309,3738],{"class":3430},[3417,12311,3742],{"class":3741},[3417,12313,3438],{"class":3430},[3417,12315,7441],{"class":3741},[3417,12317,3438],{"class":3430},[3417,12319,5200],{"class":3741},[3417,12321,3750],{"class":3430},[3417,12323,3876],{"class":3722},[3417,12325,3950],{"class":3430},[3417,12327,3953],{"class":3718},[3417,12329,3726],{"class":3430},[3417,12331,12332,12334,12336,12338,12340,12342],{"class":3419,"line":5749},[3417,12333,3961],{"class":3960},[3417,12335,6021],{"class":3718},[3417,12337,6024],{"class":3734},[3417,12339,9812],{"class":3430},[3417,12341,3876],{"class":3722},[3417,12343,3764],{"class":3430},[3417,12345,12346,12348,12350,12352,12354,12356,12358,12360,12362,12365],{"class":3419,"line":5760},[3417,12347,3974],{"class":3960},[3417,12349,6044],{"class":3722},[3417,12351,3738],{"class":3430},[3417,12353,4001],{"class":3718},[3417,12355,9833],{"class":3434},[3417,12357,4744],{"class":3718},[3417,12359,9838],{"class":3430},[3417,12361,4012],{"class":3718},[3417,12363,12364],{"class":3434},"' має бути str\"",[3417,12366,3445],{"class":3430},[3417,12368,12369],{"class":3419,"line":6271},[3417,12370,12371],{"class":3430},"        normalized = value.strip().lower()\n",[3417,12373,12374,12376,12379,12382,12385,12387],{"class":3419,"line":6277},[3417,12375,3961],{"class":3960},[3417,12377,12378],{"class":3430}," normalized.count(",[3417,12380,12381],{"class":3434},"\"@\"",[3417,12383,12384],{"class":3430},") != ",[3417,12386,10099],{"class":3441},[3417,12388,3726],{"class":3430},[3417,12390,12391,12393,12395],{"class":3419,"line":6289},[3417,12392,3974],{"class":3960},[3417,12394,3977],{"class":3722},[3417,12396,9825],{"class":3430},[3417,12398,12399,12401,12403,12405,12407,12409],{"class":3419,"line":6299},[3417,12400,9830],{"class":3718},[3417,12402,9833],{"class":3434},[3417,12404,4744],{"class":3718},[3417,12406,9838],{"class":3430},[3417,12408,4012],{"class":3718},[3417,12410,12411],{"class":3434},"': email має містити рівно один '@'\"\n",[3417,12413,12414],{"class":3419,"line":6304},[3417,12415,9868],{"class":3430},[3417,12417,12418,12421,12423],{"class":3419,"line":6310},[3417,12419,12420],{"class":3430},"        local, domain = normalized.split(",[3417,12422,12381],{"class":3434},[3417,12424,3445],{"class":3430},[3417,12426,12427,12429,12431],{"class":3419,"line":6315},[3417,12428,3961],{"class":3960},[3417,12430,6021],{"class":3718},[3417,12432,12433],{"class":3430}," local:\n",[3417,12435,12436,12438,12440,12442,12444,12446,12448,12450,12452,12455],{"class":3419,"line":6322},[3417,12437,3974],{"class":3960},[3417,12439,3977],{"class":3722},[3417,12441,3738],{"class":3430},[3417,12443,4001],{"class":3718},[3417,12445,9833],{"class":3434},[3417,12447,4744],{"class":3718},[3417,12449,9838],{"class":3430},[3417,12451,4012],{"class":3718},[3417,12453,12454],{"class":3434},"': локальна частина порожня\"",[3417,12456,3445],{"class":3430},[3417,12458,12459,12461,12464,12466,12469,12472,12474,12477,12480,12482,12484,12487,12489],{"class":3419,"line":6340},[3417,12460,3961],{"class":3960},[3417,12462,12463],{"class":3434}," \".\"",[3417,12465,6021],{"class":3718},[3417,12467,12468],{"class":3718}," in",[3417,12470,12471],{"class":3430}," domain ",[3417,12473,8056],{"class":3718},[3417,12475,12476],{"class":3430}," domain.startswith(",[3417,12478,12479],{"class":3434},"\".\"",[3417,12481,10696],{"class":3430},[3417,12483,8056],{"class":3718},[3417,12485,12486],{"class":3430}," domain.endswith(",[3417,12488,12479],{"class":3434},[3417,12490,3764],{"class":3430},[3417,12492,12493,12495,12497],{"class":3419,"line":6346},[3417,12494,3974],{"class":3960},[3417,12496,3977],{"class":3722},[3417,12498,9825],{"class":3430},[3417,12500,12501,12503,12505,12507,12509,12511,12514,12516,12519,12521],{"class":3419,"line":8484},[3417,12502,9830],{"class":3718},[3417,12504,9833],{"class":3434},[3417,12506,4744],{"class":3718},[3417,12508,9838],{"class":3430},[3417,12510,4012],{"class":3718},[3417,12512,12513],{"class":3434},"': невалідна доменна частина '",[3417,12515,4007],{"class":3718},[3417,12517,12518],{"class":3430},"domain",[3417,12520,4012],{"class":3718},[3417,12522,10037],{"class":3434},[3417,12524,12525],{"class":3419,"line":8509},[3417,12526,9868],{"class":3430},[3417,12528,12529,12531,12533,12535],{"class":3419,"line":8520},[3417,12530,9961],{"class":3734},[3417,12532,9755],{"class":3430},[3417,12534,3742],{"class":3718},[3417,12536,12537],{"class":3430},".private_name, normalized)\n",[3417,12539,12540],{"class":3419,"line":8534},[3417,12541,3452],{"emptyLinePlaceholder":3451},[3417,12543,12544],{"class":3419,"line":8542},[3417,12545,3452],{"emptyLinePlaceholder":3451},[3417,12547,12548,12550,12553],{"class":3419,"line":8547},[3417,12549,3719],{"class":3718},[3417,12551,12552],{"class":3722}," PhoneNumber",[3417,12554,3726],{"class":3430},[3417,12556,12557],{"class":3419,"line":8552},[3417,12558,12559],{"class":3434},"    \"\"\"Дескриптор: номер телефону у форматі +XXXXXXXXXXX (10-15 цифр після +).\"\"\"\n",[3417,12561,12562],{"class":3419,"line":8563},[3417,12563,3452],{"emptyLinePlaceholder":3451},[3417,12565,12566,12568,12570,12572,12574,12576,12578,12580,12582,12584,12586,12588,12590,12592,12594],{"class":3419,"line":8574},[3417,12567,3731],{"class":3718},[3417,12569,9409],{"class":3734},[3417,12571,3738],{"class":3430},[3417,12573,3742],{"class":3741},[3417,12575,3438],{"class":3430},[3417,12577,3871],{"class":3741},[3417,12579,3750],{"class":3430},[3417,12581,6056],{"class":3722},[3417,12583,3438],{"class":3430},[3417,12585,9422],{"class":3741},[3417,12587,3750],{"class":3430},[3417,12589,3876],{"class":3722},[3417,12591,3950],{"class":3430},[3417,12593,3953],{"class":3718},[3417,12595,3726],{"class":3430},[3417,12597,12598,12600],{"class":3419,"line":8583},[3417,12599,3769],{"class":3718},[3417,12601,9677],{"class":3430},[3417,12603,12604,12606,12608,12610,12612,12614,12616,12618],{"class":3419,"line":8593},[3417,12605,3769],{"class":3718},[3417,12607,9451],{"class":3430},[3417,12609,4001],{"class":3718},[3417,12611,9456],{"class":3434},[3417,12613,4007],{"class":3718},[3417,12615,9422],{"class":3430},[3417,12617,4012],{"class":3718},[3417,12619,9696],{"class":3434},[3417,12621,12622],{"class":3419,"line":8598},[3417,12623,3452],{"emptyLinePlaceholder":3451},[3417,12625,12626,12628,12630,12632,12634,12636,12638,12640,12642],{"class":3419,"line":8605},[3417,12627,3731],{"class":3718},[3417,12629,8086],{"class":3734},[3417,12631,3738],{"class":3430},[3417,12633,3742],{"class":3741},[3417,12635,3438],{"class":3430},[3417,12637,7441],{"class":3741},[3417,12639,3438],{"class":3430},[3417,12641,3871],{"class":3741},[3417,12643,3764],{"class":3430},[3417,12645,12646,12648,12650,12652,12654],{"class":3419,"line":8613},[3417,12647,3961],{"class":3960},[3417,12649,8112],{"class":3430},[3417,12651,8115],{"class":3718},[3417,12653,8073],{"class":3718},[3417,12655,3726],{"class":3430},[3417,12657,12658,12660],{"class":3419,"line":8624},[3417,12659,8124],{"class":3960},[3417,12661,8127],{"class":3718},[3417,12663,12664,12666,12668,12670,12672,12674,12676],{"class":3419,"line":10278},[3417,12665,4130],{"class":3960},[3417,12667,9752],{"class":3734},[3417,12669,9755],{"class":3430},[3417,12671,3742],{"class":3718},[3417,12673,9760],{"class":3430},[3417,12675,3953],{"class":3718},[3417,12677,3445],{"class":3430},[3417,12679,12680],{"class":3419,"line":10316},[3417,12681,3452],{"emptyLinePlaceholder":3451},[3417,12683,12684,12686,12688,12690,12692,12694,12696,12698,12700,12702,12704,12707,12709,12711,12713],{"class":3419,"line":10321},[3417,12685,3731],{"class":3718},[3417,12687,8175],{"class":3734},[3417,12689,3738],{"class":3430},[3417,12691,3742],{"class":3741},[3417,12693,3438],{"class":3430},[3417,12695,7441],{"class":3741},[3417,12697,3438],{"class":3430},[3417,12699,5200],{"class":3741},[3417,12701,3750],{"class":3430},[3417,12703,3876],{"class":3722},[3417,12705,12706],{"class":3430}," | ",[3417,12708,3953],{"class":3718},[3417,12710,3950],{"class":3430},[3417,12712,3953],{"class":3718},[3417,12714,3726],{"class":3430},[3417,12716,12717,12719,12722,12724,12726],{"class":3419,"line":10326},[3417,12718,3961],{"class":3960},[3417,12720,12721],{"class":3430}," value ",[3417,12723,8115],{"class":3718},[3417,12725,8073],{"class":3718},[3417,12727,3726],{"class":3430},[3417,12729,12730,12733,12735,12737,12739,12741],{"class":3419,"line":10331},[3417,12731,12732],{"class":3734},"            setattr",[3417,12734,9755],{"class":3430},[3417,12736,3742],{"class":3718},[3417,12738,9760],{"class":3430},[3417,12740,3953],{"class":3718},[3417,12742,3445],{"class":3430},[3417,12744,12745],{"class":3419,"line":10337},[3417,12746,12747],{"class":3960},"            return\n",[3417,12749,12750,12752,12754,12756,12758,12760],{"class":3419,"line":10360},[3417,12751,3961],{"class":3960},[3417,12753,6021],{"class":3718},[3417,12755,6024],{"class":3734},[3417,12757,9812],{"class":3430},[3417,12759,3876],{"class":3722},[3417,12761,3764],{"class":3430},[3417,12763,12764,12766,12768,12770,12772,12774,12776,12778,12780,12783],{"class":3419,"line":10368},[3417,12765,3974],{"class":3960},[3417,12767,6044],{"class":3722},[3417,12769,3738],{"class":3430},[3417,12771,4001],{"class":3718},[3417,12773,9833],{"class":3434},[3417,12775,4744],{"class":3718},[3417,12777,9838],{"class":3430},[3417,12779,4012],{"class":3718},[3417,12781,12782],{"class":3434},"' має бути str або None\"",[3417,12784,3445],{"class":3430},[3417,12786,12787],{"class":3419,"line":10373},[3417,12788,12789],{"class":3430},"        stripped = value.strip()\n",[3417,12791,12792,12794,12796,12799,12802],{"class":3419,"line":10382},[3417,12793,3961],{"class":3960},[3417,12795,6021],{"class":3718},[3417,12797,12798],{"class":3430}," stripped.startswith(",[3417,12800,12801],{"class":3434},"\"+\"",[3417,12803,3764],{"class":3430},[3417,12805,12806,12808,12810,12812,12814,12816,12818,12820,12822,12825],{"class":3419,"line":10405},[3417,12807,3974],{"class":3960},[3417,12809,3977],{"class":3722},[3417,12811,3738],{"class":3430},[3417,12813,4001],{"class":3718},[3417,12815,9833],{"class":3434},[3417,12817,4744],{"class":3718},[3417,12819,9838],{"class":3430},[3417,12821,4012],{"class":3718},[3417,12823,12824],{"class":3434},"': має починатися з '+'\"",[3417,12826,3445],{"class":3430},[3417,12828,12829,12832,12834],{"class":3419,"line":10410},[3417,12830,12831],{"class":3430},"        digits = stripped[",[3417,12833,10099],{"class":3441},[3417,12835,12836],{"class":3430},":]\n",[3417,12838,12839,12841,12843],{"class":3419,"line":10416},[3417,12840,3961],{"class":3960},[3417,12842,6021],{"class":3718},[3417,12844,12845],{"class":3430}," digits.isdigit():\n",[3417,12847,12848,12850,12852,12854,12856,12858,12860,12862,12864,12867],{"class":3419,"line":10470},[3417,12849,3974],{"class":3960},[3417,12851,3977],{"class":3722},[3417,12853,3738],{"class":3430},[3417,12855,4001],{"class":3718},[3417,12857,9833],{"class":3434},[3417,12859,4744],{"class":3718},[3417,12861,9838],{"class":3430},[3417,12863,4012],{"class":3718},[3417,12865,12866],{"class":3434},"': після '+' лише цифри\"",[3417,12868,3445],{"class":3430},[3417,12870,12871,12873,12875,12877,12880,12883,12886,12889,12892],{"class":3419,"line":10495},[3417,12872,3961],{"class":3960},[3417,12874,6021],{"class":3718},[3417,12876,6164],{"class":3430},[3417,12878,12879],{"class":3441},"10",[3417,12881,12882],{"class":3430}," \u003C= ",[3417,12884,12885],{"class":3734},"len",[3417,12887,12888],{"class":3430},"(digits) \u003C= ",[3417,12890,12891],{"class":3441},"15",[3417,12893,3764],{"class":3430},[3417,12895,12896,12898,12900],{"class":3419,"line":10500},[3417,12897,3974],{"class":3960},[3417,12899,3977],{"class":3722},[3417,12901,9825],{"class":3430},[3417,12903,12904,12906,12908,12910,12912,12914],{"class":3419,"line":10506},[3417,12905,9830],{"class":3718},[3417,12907,9833],{"class":3434},[3417,12909,4744],{"class":3718},[3417,12911,9838],{"class":3430},[3417,12913,4012],{"class":3718},[3417,12915,12916],{"class":3434},"': кількість цифр має бути 10–15, \"\n",[3417,12918,12919,12921,12923,12925,12927,12930,12932],{"class":3419,"line":10533},[3417,12920,9830],{"class":3718},[3417,12922,9850],{"class":3434},[3417,12924,4007],{"class":3718},[3417,12926,12885],{"class":3734},[3417,12928,12929],{"class":3430},"(digits)",[3417,12931,4012],{"class":3718},[3417,12933,9696],{"class":3434},[3417,12935,12936],{"class":3419,"line":10538},[3417,12937,9868],{"class":3430},[3417,12939,12940,12942,12944,12946],{"class":3419,"line":10544},[3417,12941,9961],{"class":3734},[3417,12943,9755],{"class":3430},[3417,12945,3742],{"class":3718},[3417,12947,12948],{"class":3430},".private_name, stripped)\n",[3408,12950,12953],{"className":3410,"code":12951,"filename":12952,"language":3412,"meta":3413,"style":3413},"from validators import NonEmptyString, EmailAttribute, PhoneNumber\n\nclass UserProfile:\n    \"\"\"\n    Профіль користувача з декларативною валідацією через дескриптори\n    та @property для обчислювальних полів.\n    \"\"\"\n\n    # Дескриптори (валідація автоматична при присвоєнні)\n    username   = NonEmptyString()\n    first_name = NonEmptyString()\n    last_name  = NonEmptyString()\n    email      = EmailAttribute()\n    phone      = PhoneNumber()   # може бути None\n\n    def __init__(\n        self,\n        username:   str,\n        first_name: str,\n        last_name:  str,\n        email:      str,\n        phone:      str | None = None,\n        balance:    float = 0.0,\n    ):\n        self.username   = username\n        self.first_name = first_name\n        self.last_name  = last_name\n        self.email      = email\n        self.phone      = phone\n        self.balance    = balance  # через @property.setter\n\n    # ── @property: баланс з валідацією ───────────────────────────────────────\n\n    @property\n    def balance(self) -> float:\n        return self._balance\n\n    @balance.setter\n    def balance(self, value: float) -> None:\n        if not isinstance(value, (int, float)):\n            raise TypeError(\"balance має бути числом\")\n        if value \u003C 0:\n            raise ValueError(f\"balance не може бути від'ємним, отримано {value}\")\n        self._balance = float(value)\n\n    # ── Обчислювальні властивості (read-only) ────────────────────────────────\n\n    @property\n    def full_name(self) -> str:\n        \"\"\"Повне ім'я: first_name + last_name.\"\"\"\n        return f\"{self.first_name} {self.last_name}\"\n\n    @property\n    def display_name(self) -> str:\n        \"\"\"Відображуване ім'я: 'First Last (@username)'.\"\"\"\n        return f\"{self.full_name} (@{self.username})\"\n\n    @property\n    def is_verified(self) -> bool:\n        \"\"\"Верифікований якщо є і email, і phone.\"\"\"\n        return bool(self.email and self.phone)\n\n    def __repr__(self) -> str:\n        return (\n            f\"UserProfile(username={self.username!r}, \"\n            f\"email={self.email!r}, balance={self._balance})\"\n        )\n","user_profile.py",[3399,12954,12955,12968,12972,12981,12985,12990,12995,12999,13003,13008,13013,13018,13023,13028,13036,13040,13048,13055,13066,13077,13089,13101,13120,13136,13141,13148,13155,13162,13169,13176,13186,13190,13195,13199,13205,13221,13229,13233,13237,13261,13279,13292,13302,13325,13336,13340,13345,13349,13355,13372,13377,13402,13406,13412,13429,13434,13461,13465,13471,13488,13493,13515,13519,13535,13541,13556,13582],{"__ignoreMap":3413},[3417,12956,12957,12960,12963,12965],{"class":3419,"line":3420},[3417,12958,12959],{"class":3960},"from",[3417,12961,12962],{"class":3430}," validators ",[3417,12964,5375],{"class":3960},[3417,12966,12967],{"class":3430}," NonEmptyString, EmailAttribute, PhoneNumber\n",[3417,12969,12970],{"class":3419,"line":3427},[3417,12971,3452],{"emptyLinePlaceholder":3451},[3417,12973,12974,12976,12979],{"class":3419,"line":3448},[3417,12975,3719],{"class":3718},[3417,12977,12978],{"class":3722}," UserProfile",[3417,12980,3726],{"class":3430},[3417,12982,12983],{"class":3419,"line":3455},[3417,12984,5877],{"class":3434},[3417,12986,12987],{"class":3419,"line":3461},[3417,12988,12989],{"class":3434},"    Профіль користувача з декларативною валідацією через дескриптори\n",[3417,12991,12992],{"class":3419,"line":3473},[3417,12993,12994],{"class":3434},"    та @property для обчислювальних полів.\n",[3417,12996,12997],{"class":3419,"line":3485},[3417,12998,5877],{"class":3434},[3417,13000,13001],{"class":3419,"line":3490},[3417,13002,3452],{"emptyLinePlaceholder":3451},[3417,13004,13005],{"class":3419,"line":3496},[3417,13006,13007],{"class":3423},"    # Дескриптори (валідація автоматична при присвоєнні)\n",[3417,13009,13010],{"class":3419,"line":3645},[3417,13011,13012],{"class":3430},"    username   = NonEmptyString()\n",[3417,13014,13015],{"class":3419,"line":3651},[3417,13016,13017],{"class":3430},"    first_name = NonEmptyString()\n",[3417,13019,13020],{"class":3419,"line":3657},[3417,13021,13022],{"class":3430},"    last_name  = NonEmptyString()\n",[3417,13024,13025],{"class":3419,"line":3662},[3417,13026,13027],{"class":3430},"    email      = EmailAttribute()\n",[3417,13029,13030,13033],{"class":3419,"line":3668},[3417,13031,13032],{"class":3430},"    phone      = PhoneNumber()   ",[3417,13034,13035],{"class":3423},"# може бути None\n",[3417,13037,13038],{"class":3419,"line":3673},[3417,13039,3452],{"emptyLinePlaceholder":3451},[3417,13041,13042,13044,13046],{"class":3419,"line":3679},[3417,13043,3731],{"class":3718},[3417,13045,3735],{"class":3734},[3417,13047,9825],{"class":3430},[3417,13049,13050,13052],{"class":3419,"line":3685},[3417,13051,3769],{"class":3741},[3417,13053,13054],{"class":3430},",\n",[3417,13056,13057,13060,13062,13064],{"class":3419,"line":4074},[3417,13058,13059],{"class":3741},"        username",[3417,13061,9129],{"class":3430},[3417,13063,3876],{"class":3722},[3417,13065,13054],{"class":3430},[3417,13067,13068,13071,13073,13075],{"class":3419,"line":4082},[3417,13069,13070],{"class":3741},"        first_name",[3417,13072,3750],{"class":3430},[3417,13074,3876],{"class":3722},[3417,13076,13054],{"class":3430},[3417,13078,13079,13082,13085,13087],{"class":3419,"line":4104},[3417,13080,13081],{"class":3741},"        last_name",[3417,13083,13084],{"class":3430},":  ",[3417,13086,3876],{"class":3722},[3417,13088,13054],{"class":3430},[3417,13090,13091,13094,13097,13099],{"class":3419,"line":4109},[3417,13092,13093],{"class":3741},"        email",[3417,13095,13096],{"class":3430},":      ",[3417,13098,3876],{"class":3722},[3417,13100,13054],{"class":3430},[3417,13102,13103,13106,13108,13110,13112,13114,13116,13118],{"class":3419,"line":4127},[3417,13104,13105],{"class":3741},"        phone",[3417,13107,13096],{"class":3430},[3417,13109,3876],{"class":3722},[3417,13111,12706],{"class":3430},[3417,13113,3953],{"class":3718},[3417,13115,10180],{"class":3430},[3417,13117,3953],{"class":3718},[3417,13119,13054],{"class":3430},[3417,13121,13122,13125,13128,13130,13132,13134],{"class":3419,"line":4522},[3417,13123,13124],{"class":3741},"        balance",[3417,13126,13127],{"class":3430},":    ",[3417,13129,3536],{"class":3722},[3417,13131,10180],{"class":3430},[3417,13133,6727],{"class":3441},[3417,13135,13054],{"class":3430},[3417,13137,13138],{"class":3419,"line":4527},[3417,13139,13140],{"class":3430},"    ):\n",[3417,13142,13143,13145],{"class":3419,"line":4533},[3417,13144,3769],{"class":3718},[3417,13146,13147],{"class":3430},".username   = username\n",[3417,13149,13150,13152],{"class":3419,"line":4546},[3417,13151,3769],{"class":3718},[3417,13153,13154],{"class":3430},".first_name = first_name\n",[3417,13156,13157,13159],{"class":3419,"line":4551},[3417,13158,3769],{"class":3718},[3417,13160,13161],{"class":3430},".last_name  = last_name\n",[3417,13163,13164,13166],{"class":3419,"line":4557},[3417,13165,3769],{"class":3718},[3417,13167,13168],{"class":3430},".email      = email\n",[3417,13170,13171,13173],{"class":3419,"line":4565},[3417,13172,3769],{"class":3718},[3417,13174,13175],{"class":3430},".phone      = phone\n",[3417,13177,13178,13180,13183],{"class":3419,"line":4574},[3417,13179,3769],{"class":3718},[3417,13181,13182],{"class":3430},".balance    = balance  ",[3417,13184,13185],{"class":3423},"# через @property.setter\n",[3417,13187,13188],{"class":3419,"line":5657},[3417,13189,3452],{"emptyLinePlaceholder":3451},[3417,13191,13192],{"class":3419,"line":5680},[3417,13193,13194],{"class":3423},"    # ── @property: баланс з валідацією ───────────────────────────────────────\n",[3417,13196,13197],{"class":3419,"line":5685},[3417,13198,3452],{"emptyLinePlaceholder":3451},[3417,13200,13201,13203],{"class":3419,"line":5691},[3417,13202,5452],{"class":3734},[3417,13204,5455],{"class":3722},[3417,13206,13207,13209,13211,13213,13215,13217,13219],{"class":3419,"line":5700},[3417,13208,3731],{"class":3718},[3417,13210,7082],{"class":3734},[3417,13212,3738],{"class":3430},[3417,13214,3742],{"class":3741},[3417,13216,3950],{"class":3430},[3417,13218,3536],{"class":3722},[3417,13220,3726],{"class":3430},[3417,13222,13223,13225,13227],{"class":3419,"line":5722},[3417,13224,4130],{"class":3960},[3417,13226,4133],{"class":3718},[3417,13228,4136],{"class":3430},[3417,13230,13231],{"class":3419,"line":5727},[3417,13232,3452],{"emptyLinePlaceholder":3451},[3417,13234,13235],{"class":3419,"line":5733},[3417,13236,7114],{"class":3734},[3417,13238,13239,13241,13243,13245,13247,13249,13251,13253,13255,13257,13259],{"class":3419,"line":5740},[3417,13240,3731],{"class":3718},[3417,13242,7082],{"class":3734},[3417,13244,3738],{"class":3430},[3417,13246,3742],{"class":3741},[3417,13248,3438],{"class":3430},[3417,13250,5200],{"class":3741},[3417,13252,3750],{"class":3430},[3417,13254,3536],{"class":3722},[3417,13256,3950],{"class":3430},[3417,13258,3953],{"class":3718},[3417,13260,3726],{"class":3430},[3417,13262,13263,13265,13267,13269,13271,13273,13275,13277],{"class":3419,"line":5749},[3417,13264,3961],{"class":3960},[3417,13266,6021],{"class":3718},[3417,13268,6024],{"class":3734},[3417,13270,6027],{"class":3430},[3417,13272,6030],{"class":3722},[3417,13274,3438],{"class":3430},[3417,13276,3536],{"class":3722},[3417,13278,6037],{"class":3430},[3417,13280,13281,13283,13285,13287,13290],{"class":3419,"line":5760},[3417,13282,3974],{"class":3960},[3417,13284,6044],{"class":3722},[3417,13286,3738],{"class":3430},[3417,13288,13289],{"class":3434},"\"balance має бути числом\"",[3417,13291,3445],{"class":3430},[3417,13293,13294,13296,13298,13300],{"class":3419,"line":6271},[3417,13295,3961],{"class":3960},[3417,13297,5216],{"class":3430},[3417,13299,3967],{"class":3441},[3417,13301,3726],{"class":3430},[3417,13303,13304,13306,13308,13310,13312,13315,13317,13319,13321,13323],{"class":3419,"line":6277},[3417,13305,3974],{"class":3960},[3417,13307,3977],{"class":3722},[3417,13309,3738],{"class":3430},[3417,13311,4001],{"class":3718},[3417,13313,13314],{"class":3434},"\"balance не може бути від'ємним, отримано ",[3417,13316,4007],{"class":3718},[3417,13318,5200],{"class":3430},[3417,13320,4012],{"class":3718},[3417,13322,4015],{"class":3434},[3417,13324,3445],{"class":3430},[3417,13326,13327,13329,13332,13334],{"class":3419,"line":6289},[3417,13328,3769],{"class":3718},[3417,13330,13331],{"class":3430},"._balance = ",[3417,13333,3536],{"class":3722},[3417,13335,6116],{"class":3430},[3417,13337,13338],{"class":3419,"line":6299},[3417,13339,3452],{"emptyLinePlaceholder":3451},[3417,13341,13342],{"class":3419,"line":6304},[3417,13343,13344],{"class":3423},"    # ── Обчислювальні властивості (read-only) ────────────────────────────────\n",[3417,13346,13347],{"class":3419,"line":6310},[3417,13348,3452],{"emptyLinePlaceholder":3451},[3417,13350,13351,13353],{"class":3419,"line":6315},[3417,13352,5452],{"class":3734},[3417,13354,5455],{"class":3722},[3417,13356,13357,13359,13362,13364,13366,13368,13370],{"class":3419,"line":6322},[3417,13358,3731],{"class":3718},[3417,13360,13361],{"class":3734}," full_name",[3417,13363,3738],{"class":3430},[3417,13365,3742],{"class":3741},[3417,13367,3950],{"class":3430},[3417,13369,3876],{"class":3722},[3417,13371,3726],{"class":3430},[3417,13373,13374],{"class":3419,"line":6340},[3417,13375,13376],{"class":3434},"        \"\"\"Повне ім'я: first_name + last_name.\"\"\"\n",[3417,13378,13379,13381,13383,13385,13387,13390,13392,13395,13398,13400],{"class":3419,"line":6346},[3417,13380,4130],{"class":3960},[3417,13382,11468],{"class":3718},[3417,13384,4015],{"class":3434},[3417,13386,4744],{"class":3718},[3417,13388,13389],{"class":3430},".first_name",[3417,13391,4012],{"class":3718},[3417,13393,13394],{"class":3718}," {self",[3417,13396,13397],{"class":3430},".last_name",[3417,13399,4012],{"class":3718},[3417,13401,9696],{"class":3434},[3417,13403,13404],{"class":3419,"line":8484},[3417,13405,3452],{"emptyLinePlaceholder":3451},[3417,13407,13408,13410],{"class":3419,"line":8509},[3417,13409,5452],{"class":3734},[3417,13411,5455],{"class":3722},[3417,13413,13414,13416,13419,13421,13423,13425,13427],{"class":3419,"line":8520},[3417,13415,3731],{"class":3718},[3417,13417,13418],{"class":3734}," display_name",[3417,13420,3738],{"class":3430},[3417,13422,3742],{"class":3741},[3417,13424,3950],{"class":3430},[3417,13426,3876],{"class":3722},[3417,13428,3726],{"class":3430},[3417,13430,13431],{"class":3419,"line":8534},[3417,13432,13433],{"class":3434},"        \"\"\"Відображуване ім'я: 'First Last (@username)'.\"\"\"\n",[3417,13435,13436,13438,13440,13442,13444,13447,13449,13452,13454,13457,13459],{"class":3419,"line":8542},[3417,13437,4130],{"class":3960},[3417,13439,11468],{"class":3718},[3417,13441,4015],{"class":3434},[3417,13443,4744],{"class":3718},[3417,13445,13446],{"class":3430},".full_name",[3417,13448,4012],{"class":3718},[3417,13450,13451],{"class":3434}," (@",[3417,13453,4744],{"class":3718},[3417,13455,13456],{"class":3430},".username",[3417,13458,4012],{"class":3718},[3417,13460,10313],{"class":3434},[3417,13462,13463],{"class":3419,"line":8547},[3417,13464,3452],{"emptyLinePlaceholder":3451},[3417,13466,13467,13469],{"class":3419,"line":8552},[3417,13468,5452],{"class":3734},[3417,13470,5455],{"class":3722},[3417,13472,13473,13475,13478,13480,13482,13484,13486],{"class":3419,"line":8563},[3417,13474,3731],{"class":3718},[3417,13476,13477],{"class":3734}," is_verified",[3417,13479,3738],{"class":3430},[3417,13481,3742],{"class":3741},[3417,13483,3950],{"class":3430},[3417,13485,11421],{"class":3722},[3417,13487,3726],{"class":3430},[3417,13489,13490],{"class":3419,"line":8574},[3417,13491,13492],{"class":3434},"        \"\"\"Верифікований якщо є і email, і phone.\"\"\"\n",[3417,13494,13495,13497,13500,13502,13504,13507,13510,13512],{"class":3419,"line":8583},[3417,13496,4130],{"class":3960},[3417,13498,13499],{"class":3722}," bool",[3417,13501,3738],{"class":3430},[3417,13503,3742],{"class":3718},[3417,13505,13506],{"class":3430},".email ",[3417,13508,13509],{"class":3960},"and",[3417,13511,4133],{"class":3718},[3417,13513,13514],{"class":3430},".phone)\n",[3417,13516,13517],{"class":3419,"line":8593},[3417,13518,3452],{"emptyLinePlaceholder":3451},[3417,13520,13521,13523,13525,13527,13529,13531,13533],{"class":3419,"line":8598},[3417,13522,3731],{"class":3718},[3417,13524,9979],{"class":3734},[3417,13526,3738],{"class":3430},[3417,13528,3742],{"class":3741},[3417,13530,3950],{"class":3430},[3417,13532,3876],{"class":3722},[3417,13534,3726],{"class":3430},[3417,13536,13537,13539],{"class":3419,"line":8605},[3417,13538,4130],{"class":3960},[3417,13540,9996],{"class":3430},[3417,13542,13543,13545,13548,13550,13552,13554],{"class":3419,"line":8613},[3417,13544,10001],{"class":3718},[3417,13546,13547],{"class":3434},"\"UserProfile(username=",[3417,13549,4744],{"class":3718},[3417,13551,13456],{"class":3430},[3417,13553,9861],{"class":3718},[3417,13555,10275],{"class":3434},[3417,13557,13558,13560,13563,13565,13568,13570,13573,13575,13578,13580],{"class":3419,"line":8624},[3417,13559,10001],{"class":3718},[3417,13561,13562],{"class":3434},"\"email=",[3417,13564,4744],{"class":3718},[3417,13566,13567],{"class":3430},".email",[3417,13569,9861],{"class":3718},[3417,13571,13572],{"class":3434},", balance=",[3417,13574,4744],{"class":3718},[3417,13576,13577],{"class":3430},"._balance",[3417,13579,4012],{"class":3718},[3417,13581,10313],{"class":3434},[3417,13583,13584],{"class":3419,"line":10278},[3417,13585,10042],{"class":3430},[3408,13587,13590],{"className":3410,"code":13588,"filename":13589,"language":3412,"meta":3413,"style":3413},"from user_profile import UserProfile\n\nprint(\"=== Створення валідного профілю ===\")\nuser = UserProfile(\n    username=\"arakviel\",\n    first_name=\"  Денис  \",   # зайві пробіли — автоматично прибираються\n    last_name=\"Арквіель\",\n    email=\"Denys@Example.COM\", # нормалізується до lowercase\n    phone=\"+380671234567\",\n    balance=150.50,\n)\nprint(user)\nprint(f\"display_name: {user.display_name}\")\nprint(f\"is_verified:  {user.is_verified}\")\nprint(f\"first_name (trimmed): {user.first_name!r}\")\nprint(f\"email (normalized):   {user.email!r}\")\n\nprint(\"\\n=== Перевірка валідації ===\")\ntests = [\n    (lambda: setattr(user, 'username', \"  \"),      \"username=пробіли\"),\n    (lambda: setattr(user, 'email', \"not-an-email\"),\"email=невалідний\"),\n    (lambda: setattr(user, 'phone', \"0671234567\"), \"phone без '+'\"),\n    (lambda: setattr(user, 'balance', -10.0),      \"balance=-10\"),\n]\nfor fn, label in tests:\n    try:\n        fn()\n    except (ValueError, TypeError) as e:\n        print(f\"[{label}] {type(e).__name__}: {e}\")\n","main.py",[3399,13591,13592,13604,13608,13619,13624,13636,13651,13663,13678,13690,13702,13706,13713,13735,13757,13779,13801,13805,13820,13824,13853,13881,13908,13934,13938,13948,13954,13958,13976],{"__ignoreMap":3413},[3417,13593,13594,13596,13599,13601],{"class":3419,"line":3420},[3417,13595,12959],{"class":3960},[3417,13597,13598],{"class":3430}," user_profile ",[3417,13600,5375],{"class":3960},[3417,13602,13603],{"class":3430}," UserProfile\n",[3417,13605,13606],{"class":3419,"line":3427},[3417,13607,3452],{"emptyLinePlaceholder":3451},[3417,13609,13610,13612,13614,13617],{"class":3419,"line":3448},[3417,13611,4170],{"class":3734},[3417,13613,3738],{"class":3430},[3417,13615,13616],{"class":3434},"\"=== Створення валідного профілю ===\"",[3417,13618,3445],{"class":3430},[3417,13620,13621],{"class":3419,"line":3455},[3417,13622,13623],{"class":3430},"user = UserProfile(\n",[3417,13625,13626,13629,13631,13634],{"class":3419,"line":3461},[3417,13627,13628],{"class":3741},"    username",[3417,13630,7334],{"class":3430},[3417,13632,13633],{"class":3434},"\"arakviel\"",[3417,13635,13054],{"class":3430},[3417,13637,13638,13641,13643,13646,13648],{"class":3419,"line":3473},[3417,13639,13640],{"class":3741},"    first_name",[3417,13642,7334],{"class":3430},[3417,13644,13645],{"class":3434},"\"  Денис  \"",[3417,13647,9333],{"class":3430},[3417,13649,13650],{"class":3423},"# зайві пробіли — автоматично прибираються\n",[3417,13652,13653,13656,13658,13661],{"class":3419,"line":3485},[3417,13654,13655],{"class":3741},"    last_name",[3417,13657,7334],{"class":3430},[3417,13659,13660],{"class":3434},"\"Арквіель\"",[3417,13662,13054],{"class":3430},[3417,13664,13665,13668,13670,13673,13675],{"class":3419,"line":3490},[3417,13666,13667],{"class":3741},"    email",[3417,13669,7334],{"class":3430},[3417,13671,13672],{"class":3434},"\"Denys@Example.COM\"",[3417,13674,3438],{"class":3430},[3417,13676,13677],{"class":3423},"# нормалізується до lowercase\n",[3417,13679,13680,13683,13685,13688],{"class":3419,"line":3496},[3417,13681,13682],{"class":3741},"    phone",[3417,13684,7334],{"class":3430},[3417,13686,13687],{"class":3434},"\"+380671234567\"",[3417,13689,13054],{"class":3430},[3417,13691,13692,13695,13697,13700],{"class":3419,"line":3645},[3417,13693,13694],{"class":3741},"    balance",[3417,13696,7334],{"class":3430},[3417,13698,13699],{"class":3441},"150.50",[3417,13701,13054],{"class":3430},[3417,13703,13704],{"class":3419,"line":3651},[3417,13705,3445],{"class":3430},[3417,13707,13708,13710],{"class":3419,"line":3657},[3417,13709,4170],{"class":3734},[3417,13711,13712],{"class":3430},"(user)\n",[3417,13714,13715,13717,13719,13721,13724,13726,13729,13731,13733],{"class":3419,"line":3662},[3417,13716,4170],{"class":3734},[3417,13718,3738],{"class":3430},[3417,13720,4001],{"class":3718},[3417,13722,13723],{"class":3434},"\"display_name: ",[3417,13725,4007],{"class":3718},[3417,13727,13728],{"class":3430},"user.display_name",[3417,13730,4012],{"class":3718},[3417,13732,4015],{"class":3434},[3417,13734,3445],{"class":3430},[3417,13736,13737,13739,13741,13743,13746,13748,13751,13753,13755],{"class":3419,"line":3668},[3417,13738,4170],{"class":3734},[3417,13740,3738],{"class":3430},[3417,13742,4001],{"class":3718},[3417,13744,13745],{"class":3434},"\"is_verified:  ",[3417,13747,4007],{"class":3718},[3417,13749,13750],{"class":3430},"user.is_verified",[3417,13752,4012],{"class":3718},[3417,13754,4015],{"class":3434},[3417,13756,3445],{"class":3430},[3417,13758,13759,13761,13763,13765,13768,13770,13773,13775,13777],{"class":3419,"line":3673},[3417,13760,4170],{"class":3734},[3417,13762,3738],{"class":3430},[3417,13764,4001],{"class":3718},[3417,13766,13767],{"class":3434},"\"first_name (trimmed): ",[3417,13769,4007],{"class":3718},[3417,13771,13772],{"class":3430},"user.first_name",[3417,13774,9861],{"class":3718},[3417,13776,4015],{"class":3434},[3417,13778,3445],{"class":3430},[3417,13780,13781,13783,13785,13787,13790,13792,13795,13797,13799],{"class":3419,"line":3679},[3417,13782,4170],{"class":3734},[3417,13784,3738],{"class":3430},[3417,13786,4001],{"class":3718},[3417,13788,13789],{"class":3434},"\"email (normalized):   ",[3417,13791,4007],{"class":3718},[3417,13793,13794],{"class":3430},"user.email",[3417,13796,9861],{"class":3718},[3417,13798,4015],{"class":3434},[3417,13800,3445],{"class":3430},[3417,13802,13803],{"class":3419,"line":3685},[3417,13804,3452],{"emptyLinePlaceholder":3451},[3417,13806,13807,13809,13811,13813,13815,13818],{"class":3419,"line":4074},[3417,13808,4170],{"class":3734},[3417,13810,3738],{"class":3430},[3417,13812,4015],{"class":3434},[3417,13814,6476],{"class":6475},[3417,13816,13817],{"class":3434},"=== Перевірка валідації ===\"",[3417,13819,3445],{"class":3430},[3417,13821,13822],{"class":3419,"line":4082},[3417,13823,10547],{"class":3430},[3417,13825,13826,13828,13830,13832,13834,13837,13840,13842,13845,13848,13851],{"class":3419,"line":4104},[3417,13827,10553],{"class":3430},[3417,13829,10556],{"class":3718},[3417,13831,3750],{"class":3430},[3417,13833,10561],{"class":3734},[3417,13835,13836],{"class":3430},"(user, ",[3417,13838,13839],{"class":3434},"'username'",[3417,13841,3438],{"class":3430},[3417,13843,13844],{"class":3434},"\"  \"",[3417,13846,13847],{"class":3430},"),      ",[3417,13849,13850],{"class":3434},"\"username=пробіли\"",[3417,13852,10581],{"class":3430},[3417,13854,13855,13857,13859,13861,13863,13865,13868,13870,13873,13876,13879],{"class":3419,"line":4109},[3417,13856,10553],{"class":3430},[3417,13858,10556],{"class":3718},[3417,13860,3750],{"class":3430},[3417,13862,10561],{"class":3734},[3417,13864,13836],{"class":3430},[3417,13866,13867],{"class":3434},"'email'",[3417,13869,3438],{"class":3430},[3417,13871,13872],{"class":3434},"\"not-an-email\"",[3417,13874,13875],{"class":3430},"),",[3417,13877,13878],{"class":3434},"\"email=невалідний\"",[3417,13880,10581],{"class":3430},[3417,13882,13883,13885,13887,13889,13891,13893,13896,13898,13901,13903,13906],{"class":3419,"line":4127},[3417,13884,10553],{"class":3430},[3417,13886,10556],{"class":3718},[3417,13888,3750],{"class":3430},[3417,13890,10561],{"class":3734},[3417,13892,13836],{"class":3430},[3417,13894,13895],{"class":3434},"'phone'",[3417,13897,3438],{"class":3430},[3417,13899,13900],{"class":3434},"\"0671234567\"",[3417,13902,10634],{"class":3430},[3417,13904,13905],{"class":3434},"\"phone без '+'\"",[3417,13907,10581],{"class":3430},[3417,13909,13910,13912,13914,13916,13918,13920,13923,13925,13927,13929,13932],{"class":3419,"line":4522},[3417,13911,10553],{"class":3430},[3417,13913,10556],{"class":3718},[3417,13915,3750],{"class":3430},[3417,13917,10561],{"class":3734},[3417,13919,13836],{"class":3430},[3417,13921,13922],{"class":3434},"'balance'",[3417,13924,10600],{"class":3430},[3417,13926,3812],{"class":3441},[3417,13928,13847],{"class":3430},[3417,13930,13931],{"class":3434},"\"balance=-10\"",[3417,13933,10581],{"class":3430},[3417,13935,13936],{"class":3419,"line":4527},[3417,13937,10645],{"class":3430},[3417,13939,13940,13942,13944,13946],{"class":3419,"line":4533},[3417,13941,10437],{"class":3960},[3417,13943,10658],{"class":3430},[3417,13945,10443],{"class":3960},[3417,13947,10663],{"class":3430},[3417,13949,13950,13952],{"class":3419,"line":4546},[3417,13951,10669],{"class":3960},[3417,13953,3726],{"class":3430},[3417,13955,13956],{"class":3419,"line":4551},[3417,13957,10677],{"class":3430},[3417,13959,13960,13962,13964,13966,13968,13970,13972,13974],{"class":3419,"line":4557},[3417,13961,10683],{"class":3960},[3417,13963,6164],{"class":3430},[3417,13965,10688],{"class":3722},[3417,13967,3438],{"class":3430},[3417,13969,10693],{"class":3722},[3417,13971,10696],{"class":3430},[3417,13973,10699],{"class":3960},[3417,13975,4497],{"class":3430},[3417,13977,13978,13980,13982,13984,13986,13988,13990,13992,13994,13996,13998,14000,14002,14004,14006,14008,14010,14012,14014],{"class":3419,"line":4565},[3417,13979,4734],{"class":3734},[3417,13981,3738],{"class":3430},[3417,13983,4001],{"class":3718},[3417,13985,10713],{"class":3434},[3417,13987,4007],{"class":3718},[3417,13989,10718],{"class":3430},[3417,13991,4012],{"class":3718},[3417,13993,10723],{"class":3434},[3417,13995,4007],{"class":3718},[3417,13997,6056],{"class":3722},[3417,13999,10730],{"class":3430},[3417,14001,6062],{"class":3741},[3417,14003,4012],{"class":3718},[3417,14005,3750],{"class":3434},[3417,14007,4007],{"class":3718},[3417,14009,4513],{"class":3430},[3417,14011,4012],{"class":3718},[3417,14013,4015],{"class":3434},[3417,14015,3445],{"class":3430},[4189,14017,14019,14027,14031,14048,14056,14063,14075,14086,14089,14093,14101,14109,14117],{"title":14018},"python main.py",[4193,14020,14022,4201,14025],{"className":14021},[3419],[3417,14023,4200],{"className":14024},[4199],[3516,14026,14018],{},[4193,14028,14030],{"className":14029},[3419],"=== Створення валідного профілю ===",[4193,14032,14034,14035,14039,14040,13572,14044,10775],{"className":14033},[3419],"UserProfile(username=",[3417,14036,14038],{"className":14037},[4607],"'arakviel'",", email=",[3417,14041,14043],{"className":14042},[4607],"'denys@example.com'",[3417,14045,14047],{"className":14046},[4607],"150.5",[4193,14049,14051,14052],{"className":14050},[3419],"display_name: ",[3417,14053,14055],{"className":14054},[4624],"Денис Арквіель (@arakviel)",[4193,14057,14059,14060],{"className":14058},[3419],"is_verified:  ",[3417,14061,11828],{"className":14062},[4607],[4193,14064,14066,14067,4625,14071],{"className":14065},[3419],"first_name (trimmed): ",[3417,14068,14070],{"className":14069},[4607],"'Денис'",[3417,14072,14074],{"className":14073},[4218],"# пробіли прибрані!",[4193,14076,14078,14079,4625,14082],{"className":14077},[3419],"email (normalized):   ",[3417,14080,14043],{"className":14081},[4607],[3417,14083,14085],{"className":14084},[4218],"# lowercase!",[4193,14087],{"className":14088},[3419],[4193,14090,14092],{"className":14091},[3419],"=== Перевірка валідації ===",[4193,14094,14096,14097],{"className":14095},[3419],"[username=пробіли]    ",[3417,14098,14100],{"className":14099},[4598],"ValueError: 'username' не може бути порожнім",[4193,14102,14104,14105],{"className":14103},[3419],"[email=невалідний]    ",[3417,14106,14108],{"className":14107},[4598],"ValueError: 'email': email має містити рівно один '@'",[4193,14110,14112,14113],{"className":14111},[3419],"[phone без '+']      ",[3417,14114,14116],{"className":14115},[4598],"ValueError: 'phone': має починатися з '+'",[4193,14118,14120,14121],{"className":14119},[3419],"[balance=-10]        ",[3417,14122,14124],{"className":14123},[4598],"ValueError: balance не може бути від'ємним, отримано -10.0",[3553,14126],{},[3560,14128,14130],{"id":14129},"рівень-3-advanced-універсальний-дескриптор-з-декларативними-правилами","Рівень 3 (Advanced): Універсальний дескриптор з декларативними правилами",[3395,14132,14133,14134,14137],{},"Реалізуйте ",[3399,14135,14136],{},"ValidatedField"," — дескриптор загального призначення, що приймає список функцій-валідаторів. Це архітектура, що використовується у спрощених ORM-системах (Django fields, SQLAlchemy columns).",[3408,14139,14141],{"className":3410,"code":14140,"language":3412,"meta":3413,"style":3413},"# validated_field.py\nfrom typing import Any, Callable\n\n\n# Тип: функція-валідатор повертає None (успіх) або рядок (повідомлення про помилку)\nValidator = Callable[[Any], str | None]\n\n\ndef min_value(minimum: float) -> Validator:\n    def validate(value) -> str | None:\n        if value \u003C minimum:\n            return f\"Значення {value} менше мінімуму {minimum}\"\n    return validate\n\ndef max_value(maximum: float) -> Validator:\n    def validate(value) -> str | None:\n        if value > maximum:\n            return f\"Значення {value} перевищує максимум {maximum}\"\n    return validate\n\ndef is_type(*types) -> Validator:\n    def validate(value) -> str | None:\n        if not isinstance(value, types):\n            names = \" | \".join(t.__name__ for t in types)\n            return f\"Очікується {names}, отримано {type(value).__name__}\"\n    return validate\n\ndef not_empty() -> Validator:\n    def validate(value) -> str | None:\n        if hasattr(value, '__len__') and len(value) == 0:\n            return \"Значення не може бути порожнім\"\n        if isinstance(value, str) and not value.strip():\n            return \"Рядок не може складатися лише з пробілів\"\n    return validate\n\ndef regex_match(pattern: str) -> Validator:\n    import re\n    compiled = re.compile(pattern)\n    def validate(value) -> str | None:\n        if not compiled.match(str(value)):\n            return f\"Значення '{value}' не відповідає шаблону '{pattern}'\"\n    return validate\n\n\nclass ValidatedField:\n    \"\"\"\n    Універсальний дескриптор з декларативними правилами валідації.\n    Приймає довільну кількість функцій-валідаторів.\n    \"\"\"\n\n    def __init__(self, *validators: Validator, default=None):\n        self.validators   = validators\n        self.default      = default\n        self.public_name  = None\n        self.private_name = None\n\n    def __set_name__(self, owner: type, name: str) -> None:\n        self.public_name  = name\n        self.private_name = f\"_{name}\"\n\n    def __get__(self, instance, owner):\n        if instance is None:\n            return self\n        return getattr(instance, self.private_name, self.default)\n\n    def __set__(self, instance, value) -> None:\n        errors = []\n        for validator in self.validators:\n            error = validator(value)\n            if error is not None:\n                errors.append(error)\n        if errors:\n            raise ValueError(\n                f\"Поле '{self.public_name}': \" + \"; \".join(errors)\n            )\n        setattr(instance, self.private_name, value)\n\n    def __repr__(self) -> str:\n        return (\n            f\"ValidatedField({len(self.validators)} validators\"\n            f\"{', default=' + repr(self.default) if self.default is not None else ''})\"\n        )\n\n\n# ── Застосування: декларативна модель продукту ────────────────────────────────\n\nclass Product:\n    name = ValidatedField(\n        is_type(str),\n        not_empty(),\n    )\n    price = ValidatedField(\n        is_type(int, float),\n        min_value(0.01),\n        max_value(1_000_000),\n    )\n    sku = ValidatedField(\n        is_type(str),\n        regex_match(r\"^[A-Z]{2,4}-\\d{4,8}$\"),\n    )\n    stock = ValidatedField(\n        is_type(int),\n        min_value(0),\n        default=0,\n    )\n\n    def __init__(self, name: str, price: float, sku: str, stock: int = 0):\n        self.name  = name\n        self.price = price\n        self.sku   = sku\n        self.stock = stock\n\n    def __repr__(self) -> str:\n        return f\"Product({self.name!r}, price={self.price}, sku={self.sku!r})\"\n\n\n# Тести\nprint(\"=== Валідний продукт ===\")\np = Product(\"MacBook Pro\", 89999.0, \"MBP-20242\", stock=5)\nprint(p)\nprint(f\"stock (default worked): {p.stock}\")\n\nprint(\"\\n=== Тести валідації ===\")\nerrors_tests = [\n    (lambda: setattr(p, 'price', -1),      \"price=-1\"),\n    (lambda: setattr(p, 'price', 2_000_000),\"price=2M (>max)\"),\n    (lambda: setattr(p, 'sku', \"invalid\"), \"sku='invalid'\"),\n    (lambda: setattr(p, 'stock', -5),      \"stock=-5\"),\n    (lambda: setattr(p, 'name', \"\"),       \"name=''\"),\n]\nfor fn, label in errors_tests:\n    try:\n        fn()\n    except ValueError as e:\n        print(f\"[{label}]\\n  {e}\")\n",[3399,14142,14143,14148,14160,14164,14168,14173,14186,14190,14194,14213,14234,14241,14267,14274,14278,14296,14316,14323,14348,14354,14358,14373,14393,14404,14428,14459,14465,14469,14479,14499,14525,14532,14551,14558,14564,14568,14586,14594,14599,14619,14633,14659,14665,14669,14673,14682,14686,14691,14696,14700,14704,14732,14739,14746,14754,14762,14766,14798,14804,14822,14826,14846,14858,14864,14881,14885,14909,14914,14929,14934,14950,14955,14962,14970,14995,14999,15009,15013,15029,15035,15058,15105,15109,15113,15117,15122,15126,15135,15140,15149,15154,15159,15164,15176,15186,15196,15200,15206,15215,15252,15257,15263,15272,15281,15293,15298,15303,15355,15363,15371,15379,15387,15392,15409,15447,15452,15457,15462,15474,15503,15511,15534,15539,15555,15561,15589,15616,15644,15671,15699,15704,15716,15723,15728,15739],{"__ignoreMap":3413},[3417,14144,14145],{"class":3419,"line":3420},[3417,14146,14147],{"class":3423},"# validated_field.py\n",[3417,14149,14150,14152,14155,14157],{"class":3419,"line":3427},[3417,14151,12959],{"class":3960},[3417,14153,14154],{"class":3430}," typing ",[3417,14156,5375],{"class":3960},[3417,14158,14159],{"class":3430}," Any, Callable\n",[3417,14161,14162],{"class":3419,"line":3448},[3417,14163,3452],{"emptyLinePlaceholder":3451},[3417,14165,14166],{"class":3419,"line":3455},[3417,14167,3452],{"emptyLinePlaceholder":3451},[3417,14169,14170],{"class":3419,"line":3461},[3417,14171,14172],{"class":3423},"# Тип: функція-валідатор повертає None (успіх) або рядок (повідомлення про помилку)\n",[3417,14174,14175,14178,14180,14182,14184],{"class":3419,"line":3473},[3417,14176,14177],{"class":3430},"Validator = Callable[[Any], ",[3417,14179,3876],{"class":3722},[3417,14181,12706],{"class":3430},[3417,14183,3953],{"class":3718},[3417,14185,10645],{"class":3430},[3417,14187,14188],{"class":3419,"line":3485},[3417,14189,3452],{"emptyLinePlaceholder":3451},[3417,14191,14192],{"class":3419,"line":3490},[3417,14193,3452],{"emptyLinePlaceholder":3451},[3417,14195,14196,14198,14201,14203,14206,14208,14210],{"class":3419,"line":3496},[3417,14197,6940],{"class":3718},[3417,14199,14200],{"class":3734}," min_value",[3417,14202,3738],{"class":3430},[3417,14204,14205],{"class":3741},"minimum",[3417,14207,3750],{"class":3430},[3417,14209,3536],{"class":3722},[3417,14211,14212],{"class":3430},") -> Validator:\n",[3417,14214,14215,14217,14220,14222,14224,14226,14228,14230,14232],{"class":3419,"line":3645},[3417,14216,3731],{"class":3718},[3417,14218,14219],{"class":3734}," validate",[3417,14221,3738],{"class":3430},[3417,14223,5200],{"class":3741},[3417,14225,3950],{"class":3430},[3417,14227,3876],{"class":3722},[3417,14229,12706],{"class":3430},[3417,14231,3953],{"class":3718},[3417,14233,3726],{"class":3430},[3417,14235,14236,14238],{"class":3419,"line":3651},[3417,14237,3961],{"class":3960},[3417,14239,14240],{"class":3430}," value \u003C minimum:\n",[3417,14242,14243,14245,14247,14250,14252,14254,14256,14259,14261,14263,14265],{"class":3419,"line":3657},[3417,14244,8124],{"class":3960},[3417,14246,11468],{"class":3718},[3417,14248,14249],{"class":3434},"\"Значення ",[3417,14251,4007],{"class":3718},[3417,14253,5200],{"class":3430},[3417,14255,4012],{"class":3718},[3417,14257,14258],{"class":3434}," менше мінімуму ",[3417,14260,4007],{"class":3718},[3417,14262,14205],{"class":3430},[3417,14264,4012],{"class":3718},[3417,14266,9696],{"class":3434},[3417,14268,14269,14271],{"class":3419,"line":3662},[3417,14270,6957],{"class":3960},[3417,14272,14273],{"class":3430}," validate\n",[3417,14275,14276],{"class":3419,"line":3668},[3417,14277,3452],{"emptyLinePlaceholder":3451},[3417,14279,14280,14282,14285,14287,14290,14292,14294],{"class":3419,"line":3673},[3417,14281,6940],{"class":3718},[3417,14283,14284],{"class":3734}," max_value",[3417,14286,3738],{"class":3430},[3417,14288,14289],{"class":3741},"maximum",[3417,14291,3750],{"class":3430},[3417,14293,3536],{"class":3722},[3417,14295,14212],{"class":3430},[3417,14297,14298,14300,14302,14304,14306,14308,14310,14312,14314],{"class":3419,"line":3679},[3417,14299,3731],{"class":3718},[3417,14301,14219],{"class":3734},[3417,14303,3738],{"class":3430},[3417,14305,5200],{"class":3741},[3417,14307,3950],{"class":3430},[3417,14309,3876],{"class":3722},[3417,14311,12706],{"class":3430},[3417,14313,3953],{"class":3718},[3417,14315,3726],{"class":3430},[3417,14317,14318,14320],{"class":3419,"line":3685},[3417,14319,3961],{"class":3960},[3417,14321,14322],{"class":3430}," value > maximum:\n",[3417,14324,14325,14327,14329,14331,14333,14335,14337,14340,14342,14344,14346],{"class":3419,"line":4074},[3417,14326,8124],{"class":3960},[3417,14328,11468],{"class":3718},[3417,14330,14249],{"class":3434},[3417,14332,4007],{"class":3718},[3417,14334,5200],{"class":3430},[3417,14336,4012],{"class":3718},[3417,14338,14339],{"class":3434}," перевищує максимум ",[3417,14341,4007],{"class":3718},[3417,14343,14289],{"class":3430},[3417,14345,4012],{"class":3718},[3417,14347,9696],{"class":3434},[3417,14349,14350,14352],{"class":3419,"line":4082},[3417,14351,6957],{"class":3960},[3417,14353,14273],{"class":3430},[3417,14355,14356],{"class":3419,"line":4104},[3417,14357,3452],{"emptyLinePlaceholder":3451},[3417,14359,14360,14362,14365,14368,14371],{"class":3419,"line":4109},[3417,14361,6940],{"class":3718},[3417,14363,14364],{"class":3734}," is_type",[3417,14366,14367],{"class":3430},"(*",[3417,14369,14370],{"class":3741},"types",[3417,14372,14212],{"class":3430},[3417,14374,14375,14377,14379,14381,14383,14385,14387,14389,14391],{"class":3419,"line":4127},[3417,14376,3731],{"class":3718},[3417,14378,14219],{"class":3734},[3417,14380,3738],{"class":3430},[3417,14382,5200],{"class":3741},[3417,14384,3950],{"class":3430},[3417,14386,3876],{"class":3722},[3417,14388,12706],{"class":3430},[3417,14390,3953],{"class":3718},[3417,14392,3726],{"class":3430},[3417,14394,14395,14397,14399,14401],{"class":3419,"line":4522},[3417,14396,3961],{"class":3960},[3417,14398,6021],{"class":3718},[3417,14400,6024],{"class":3734},[3417,14402,14403],{"class":3430},"(value, types):\n",[3417,14405,14406,14409,14412,14415,14417,14420,14423,14425],{"class":3419,"line":4527},[3417,14407,14408],{"class":3430},"            names = ",[3417,14410,14411],{"class":3434},"\" | \"",[3417,14413,14414],{"class":3430},".join(t.",[3417,14416,6062],{"class":3741},[3417,14418,14419],{"class":3960}," for",[3417,14421,14422],{"class":3430}," t ",[3417,14424,10443],{"class":3960},[3417,14426,14427],{"class":3430}," types)\n",[3417,14429,14430,14432,14434,14437,14439,14442,14444,14447,14449,14451,14453,14455,14457],{"class":3419,"line":4533},[3417,14431,8124],{"class":3960},[3417,14433,11468],{"class":3718},[3417,14435,14436],{"class":3434},"\"Очікується ",[3417,14438,4007],{"class":3718},[3417,14440,14441],{"class":3430},"names",[3417,14443,4012],{"class":3718},[3417,14445,14446],{"class":3434},", отримано ",[3417,14448,4007],{"class":3718},[3417,14450,6056],{"class":3722},[3417,14452,6059],{"class":3430},[3417,14454,6062],{"class":3741},[3417,14456,4012],{"class":3718},[3417,14458,9696],{"class":3434},[3417,14460,14461,14463],{"class":3419,"line":4546},[3417,14462,6957],{"class":3960},[3417,14464,14273],{"class":3430},[3417,14466,14467],{"class":3419,"line":4551},[3417,14468,3452],{"emptyLinePlaceholder":3451},[3417,14470,14471,14473,14476],{"class":3419,"line":4557},[3417,14472,6940],{"class":3718},[3417,14474,14475],{"class":3734}," not_empty",[3417,14477,14478],{"class":3430},"() -> Validator:\n",[3417,14480,14481,14483,14485,14487,14489,14491,14493,14495,14497],{"class":3419,"line":4565},[3417,14482,3731],{"class":3718},[3417,14484,14219],{"class":3734},[3417,14486,3738],{"class":3430},[3417,14488,5200],{"class":3741},[3417,14490,3950],{"class":3430},[3417,14492,3876],{"class":3722},[3417,14494,12706],{"class":3430},[3417,14496,3953],{"class":3718},[3417,14498,3726],{"class":3430},[3417,14500,14501,14503,14506,14508,14511,14513,14515,14518,14521,14523],{"class":3419,"line":4574},[3417,14502,3961],{"class":3960},[3417,14504,14505],{"class":3734}," hasattr",[3417,14507,9812],{"class":3430},[3417,14509,14510],{"class":3434},"'__len__'",[3417,14512,10696],{"class":3430},[3417,14514,13509],{"class":3718},[3417,14516,14517],{"class":3734}," len",[3417,14519,14520],{"class":3430},"(value) == ",[3417,14522,3967],{"class":3441},[3417,14524,3726],{"class":3430},[3417,14526,14527,14529],{"class":3419,"line":5657},[3417,14528,8124],{"class":3960},[3417,14530,14531],{"class":3434}," \"Значення не може бути порожнім\"\n",[3417,14533,14534,14536,14538,14540,14542,14544,14546,14548],{"class":3419,"line":5680},[3417,14535,3961],{"class":3960},[3417,14537,6024],{"class":3734},[3417,14539,9812],{"class":3430},[3417,14541,3876],{"class":3722},[3417,14543,10696],{"class":3430},[3417,14545,13509],{"class":3718},[3417,14547,6021],{"class":3718},[3417,14549,14550],{"class":3430}," value.strip():\n",[3417,14552,14553,14555],{"class":3419,"line":5685},[3417,14554,8124],{"class":3960},[3417,14556,14557],{"class":3434}," \"Рядок не може складатися лише з пробілів\"\n",[3417,14559,14560,14562],{"class":3419,"line":5691},[3417,14561,6957],{"class":3960},[3417,14563,14273],{"class":3430},[3417,14565,14566],{"class":3419,"line":5700},[3417,14567,3452],{"emptyLinePlaceholder":3451},[3417,14569,14570,14572,14575,14577,14580,14582,14584],{"class":3419,"line":5722},[3417,14571,6940],{"class":3718},[3417,14573,14574],{"class":3734}," regex_match",[3417,14576,3738],{"class":3430},[3417,14578,14579],{"class":3741},"pattern",[3417,14581,3750],{"class":3430},[3417,14583,3876],{"class":3722},[3417,14585,14212],{"class":3430},[3417,14587,14588,14591],{"class":3419,"line":5727},[3417,14589,14590],{"class":3960},"    import",[3417,14592,14593],{"class":3430}," re\n",[3417,14595,14596],{"class":3419,"line":5733},[3417,14597,14598],{"class":3430},"    compiled = re.compile(pattern)\n",[3417,14600,14601,14603,14605,14607,14609,14611,14613,14615,14617],{"class":3419,"line":5740},[3417,14602,3731],{"class":3718},[3417,14604,14219],{"class":3734},[3417,14606,3738],{"class":3430},[3417,14608,5200],{"class":3741},[3417,14610,3950],{"class":3430},[3417,14612,3876],{"class":3722},[3417,14614,12706],{"class":3430},[3417,14616,3953],{"class":3718},[3417,14618,3726],{"class":3430},[3417,14620,14621,14623,14625,14628,14630],{"class":3419,"line":5749},[3417,14622,3961],{"class":3960},[3417,14624,6021],{"class":3718},[3417,14626,14627],{"class":3430}," compiled.match(",[3417,14629,3876],{"class":3722},[3417,14631,14632],{"class":3430},"(value)):\n",[3417,14634,14635,14637,14639,14642,14644,14646,14648,14651,14653,14655,14657],{"class":3419,"line":5760},[3417,14636,8124],{"class":3960},[3417,14638,11468],{"class":3718},[3417,14640,14641],{"class":3434},"\"Значення '",[3417,14643,4007],{"class":3718},[3417,14645,5200],{"class":3430},[3417,14647,4012],{"class":3718},[3417,14649,14650],{"class":3434},"' не відповідає шаблону '",[3417,14652,4007],{"class":3718},[3417,14654,14579],{"class":3430},[3417,14656,4012],{"class":3718},[3417,14658,10037],{"class":3434},[3417,14660,14661,14663],{"class":3419,"line":6271},[3417,14662,6957],{"class":3960},[3417,14664,14273],{"class":3430},[3417,14666,14667],{"class":3419,"line":6277},[3417,14668,3452],{"emptyLinePlaceholder":3451},[3417,14670,14671],{"class":3419,"line":6289},[3417,14672,3452],{"emptyLinePlaceholder":3451},[3417,14674,14675,14677,14680],{"class":3419,"line":6299},[3417,14676,3719],{"class":3718},[3417,14678,14679],{"class":3722}," ValidatedField",[3417,14681,3726],{"class":3430},[3417,14683,14684],{"class":3419,"line":6304},[3417,14685,5877],{"class":3434},[3417,14687,14688],{"class":3419,"line":6310},[3417,14689,14690],{"class":3434},"    Універсальний дескриптор з декларативними правилами валідації.\n",[3417,14692,14693],{"class":3419,"line":6315},[3417,14694,14695],{"class":3434},"    Приймає довільну кількість функцій-валідаторів.\n",[3417,14697,14698],{"class":3419,"line":6322},[3417,14699,5877],{"class":3434},[3417,14701,14702],{"class":3419,"line":6340},[3417,14703,3452],{"emptyLinePlaceholder":3451},[3417,14705,14706,14708,14710,14712,14714,14717,14720,14723,14726,14728,14730],{"class":3419,"line":6346},[3417,14707,3731],{"class":3718},[3417,14709,3735],{"class":3734},[3417,14711,3738],{"class":3430},[3417,14713,3742],{"class":3741},[3417,14715,14716],{"class":3430},", *",[3417,14718,14719],{"class":3741},"validators",[3417,14721,14722],{"class":3430},": Validator, ",[3417,14724,14725],{"class":3741},"default",[3417,14727,7334],{"class":3430},[3417,14729,3953],{"class":3718},[3417,14731,3764],{"class":3430},[3417,14733,14734,14736],{"class":3419,"line":8484},[3417,14735,3769],{"class":3718},[3417,14737,14738],{"class":3430},".validators   = validators\n",[3417,14740,14741,14743],{"class":3419,"line":8509},[3417,14742,3769],{"class":3718},[3417,14744,14745],{"class":3430},".default      = default\n",[3417,14747,14748,14750,14752],{"class":3419,"line":8520},[3417,14749,3769],{"class":3718},[3417,14751,9618],{"class":3430},[3417,14753,9621],{"class":3718},[3417,14755,14756,14758,14760],{"class":3419,"line":8534},[3417,14757,3769],{"class":3718},[3417,14759,9451],{"class":3430},[3417,14761,9621],{"class":3718},[3417,14763,14764],{"class":3419,"line":8542},[3417,14765,3452],{"emptyLinePlaceholder":3451},[3417,14767,14768,14770,14772,14774,14776,14778,14780,14782,14784,14786,14788,14790,14792,14794,14796],{"class":3419,"line":8547},[3417,14769,3731],{"class":3718},[3417,14771,9409],{"class":3734},[3417,14773,3738],{"class":3430},[3417,14775,3742],{"class":3741},[3417,14777,3438],{"class":3430},[3417,14779,3871],{"class":3741},[3417,14781,3750],{"class":3430},[3417,14783,6056],{"class":3722},[3417,14785,3438],{"class":3430},[3417,14787,9422],{"class":3741},[3417,14789,3750],{"class":3430},[3417,14791,3876],{"class":3722},[3417,14793,3950],{"class":3430},[3417,14795,3953],{"class":3718},[3417,14797,3726],{"class":3430},[3417,14799,14800,14802],{"class":3419,"line":8552},[3417,14801,3769],{"class":3718},[3417,14803,9677],{"class":3430},[3417,14805,14806,14808,14810,14812,14814,14816,14818,14820],{"class":3419,"line":8563},[3417,14807,3769],{"class":3718},[3417,14809,9451],{"class":3430},[3417,14811,4001],{"class":3718},[3417,14813,9456],{"class":3434},[3417,14815,4007],{"class":3718},[3417,14817,9422],{"class":3430},[3417,14819,4012],{"class":3718},[3417,14821,9696],{"class":3434},[3417,14823,14824],{"class":3419,"line":8574},[3417,14825,3452],{"emptyLinePlaceholder":3451},[3417,14827,14828,14830,14832,14834,14836,14838,14840,14842,14844],{"class":3419,"line":8583},[3417,14829,3731],{"class":3718},[3417,14831,8086],{"class":3734},[3417,14833,3738],{"class":3430},[3417,14835,3742],{"class":3741},[3417,14837,3438],{"class":3430},[3417,14839,7441],{"class":3741},[3417,14841,3438],{"class":3430},[3417,14843,3871],{"class":3741},[3417,14845,3764],{"class":3430},[3417,14847,14848,14850,14852,14854,14856],{"class":3419,"line":8593},[3417,14849,3961],{"class":3960},[3417,14851,8112],{"class":3430},[3417,14853,8115],{"class":3718},[3417,14855,8073],{"class":3718},[3417,14857,3726],{"class":3430},[3417,14859,14860,14862],{"class":3419,"line":8598},[3417,14861,8124],{"class":3960},[3417,14863,8127],{"class":3718},[3417,14865,14866,14868,14870,14872,14874,14876,14878],{"class":3419,"line":8605},[3417,14867,4130],{"class":3960},[3417,14869,9752],{"class":3734},[3417,14871,9755],{"class":3430},[3417,14873,3742],{"class":3718},[3417,14875,9760],{"class":3430},[3417,14877,3742],{"class":3718},[3417,14879,14880],{"class":3430},".default)\n",[3417,14882,14883],{"class":3419,"line":8613},[3417,14884,3452],{"emptyLinePlaceholder":3451},[3417,14886,14887,14889,14891,14893,14895,14897,14899,14901,14903,14905,14907],{"class":3419,"line":8624},[3417,14888,3731],{"class":3718},[3417,14890,8175],{"class":3734},[3417,14892,3738],{"class":3430},[3417,14894,3742],{"class":3741},[3417,14896,3438],{"class":3430},[3417,14898,7441],{"class":3741},[3417,14900,3438],{"class":3430},[3417,14902,5200],{"class":3741},[3417,14904,3950],{"class":3430},[3417,14906,3953],{"class":3718},[3417,14908,3726],{"class":3430},[3417,14910,14911],{"class":3419,"line":10278},[3417,14912,14913],{"class":3430},"        errors = []\n",[3417,14915,14916,14919,14922,14924,14926],{"class":3419,"line":10316},[3417,14917,14918],{"class":3960},"        for",[3417,14920,14921],{"class":3430}," validator ",[3417,14923,10443],{"class":3960},[3417,14925,4133],{"class":3718},[3417,14927,14928],{"class":3430},".validators:\n",[3417,14930,14931],{"class":3419,"line":10321},[3417,14932,14933],{"class":3430},"            error = validator(value)\n",[3417,14935,14936,14939,14942,14944,14946,14948],{"class":3419,"line":10326},[3417,14937,14938],{"class":3960},"            if",[3417,14940,14941],{"class":3430}," error ",[3417,14943,8115],{"class":3718},[3417,14945,6021],{"class":3718},[3417,14947,8073],{"class":3718},[3417,14949,3726],{"class":3430},[3417,14951,14952],{"class":3419,"line":10331},[3417,14953,14954],{"class":3430},"                errors.append(error)\n",[3417,14956,14957,14959],{"class":3419,"line":10337},[3417,14958,3961],{"class":3960},[3417,14960,14961],{"class":3430}," errors:\n",[3417,14963,14964,14966,14968],{"class":3419,"line":10360},[3417,14965,3974],{"class":3960},[3417,14967,3977],{"class":3722},[3417,14969,9825],{"class":3430},[3417,14971,14972,14974,14977,14979,14981,14983,14986,14989,14992],{"class":3419,"line":10368},[3417,14973,9830],{"class":3718},[3417,14975,14976],{"class":3434},"\"Поле '",[3417,14978,4744],{"class":3718},[3417,14980,9838],{"class":3430},[3417,14982,4012],{"class":3718},[3417,14984,14985],{"class":3434},"': \"",[3417,14987,14988],{"class":3430}," + ",[3417,14990,14991],{"class":3434},"\"; \"",[3417,14993,14994],{"class":3430},".join(errors)\n",[3417,14996,14997],{"class":3419,"line":10373},[3417,14998,9868],{"class":3430},[3417,15000,15001,15003,15005,15007],{"class":3419,"line":10382},[3417,15002,9961],{"class":3734},[3417,15004,9755],{"class":3430},[3417,15006,3742],{"class":3718},[3417,15008,9968],{"class":3430},[3417,15010,15011],{"class":3419,"line":10405},[3417,15012,3452],{"emptyLinePlaceholder":3451},[3417,15014,15015,15017,15019,15021,15023,15025,15027],{"class":3419,"line":10410},[3417,15016,3731],{"class":3718},[3417,15018,9979],{"class":3734},[3417,15020,3738],{"class":3430},[3417,15022,3742],{"class":3741},[3417,15024,3950],{"class":3430},[3417,15026,3876],{"class":3722},[3417,15028,3726],{"class":3430},[3417,15030,15031,15033],{"class":3419,"line":10416},[3417,15032,4130],{"class":3960},[3417,15034,9996],{"class":3430},[3417,15036,15037,15039,15042,15044,15046,15048,15050,15053,15055],{"class":3419,"line":10470},[3417,15038,10001],{"class":3718},[3417,15040,15041],{"class":3434},"\"ValidatedField(",[3417,15043,4007],{"class":3718},[3417,15045,12885],{"class":3734},[3417,15047,3738],{"class":3430},[3417,15049,3742],{"class":3718},[3417,15051,15052],{"class":3430},".validators)",[3417,15054,4012],{"class":3718},[3417,15056,15057],{"class":3434}," validators\"\n",[3417,15059,15060,15062,15064,15066,15069,15071,15074,15076,15078,15081,15084,15086,15089,15091,15093,15095,15098,15101,15103],{"class":3419,"line":10495},[3417,15061,10001],{"class":3718},[3417,15063,4015],{"class":3434},[3417,15065,4007],{"class":3718},[3417,15067,15068],{"class":3434},"', default='",[3417,15070,14988],{"class":3430},[3417,15072,15073],{"class":3734},"repr",[3417,15075,3738],{"class":3430},[3417,15077,3742],{"class":3718},[3417,15079,15080],{"class":3430},".default) ",[3417,15082,15083],{"class":3960},"if",[3417,15085,4133],{"class":3718},[3417,15087,15088],{"class":3430},".default ",[3417,15090,8115],{"class":3718},[3417,15092,6021],{"class":3718},[3417,15094,8073],{"class":3718},[3417,15096,15097],{"class":3960}," else",[3417,15099,15100],{"class":3434}," ''",[3417,15102,4012],{"class":3718},[3417,15104,10313],{"class":3434},[3417,15106,15107],{"class":3419,"line":10500},[3417,15108,10042],{"class":3430},[3417,15110,15111],{"class":3419,"line":10506},[3417,15112,3452],{"emptyLinePlaceholder":3451},[3417,15114,15115],{"class":3419,"line":10533},[3417,15116,3452],{"emptyLinePlaceholder":3451},[3417,15118,15119],{"class":3419,"line":10538},[3417,15120,15121],{"class":3423},"# ── Застосування: декларативна модель продукту ────────────────────────────────\n",[3417,15123,15124],{"class":3419,"line":10544},[3417,15125,3452],{"emptyLinePlaceholder":3451},[3417,15127,15128,15130,15133],{"class":3419,"line":10550},[3417,15129,3719],{"class":3718},[3417,15131,15132],{"class":3722}," Product",[3417,15134,3726],{"class":3430},[3417,15136,15137],{"class":3419,"line":10584},[3417,15138,15139],{"class":3430},"    name = ValidatedField(\n",[3417,15141,15142,15145,15147],{"class":3419,"line":10613},[3417,15143,15144],{"class":3430},"        is_type(",[3417,15146,3876],{"class":3722},[3417,15148,10581],{"class":3430},[3417,15150,15151],{"class":3419,"line":10642},[3417,15152,15153],{"class":3430},"        not_empty(),\n",[3417,15155,15156],{"class":3419,"line":10648},[3417,15157,15158],{"class":3430},"    )\n",[3417,15160,15161],{"class":3419,"line":10653},[3417,15162,15163],{"class":3430},"    price = ValidatedField(\n",[3417,15165,15166,15168,15170,15172,15174],{"class":3419,"line":10666},[3417,15167,15144],{"class":3430},[3417,15169,6030],{"class":3722},[3417,15171,3438],{"class":3430},[3417,15173,3536],{"class":3722},[3417,15175,10581],{"class":3430},[3417,15177,15178,15181,15184],{"class":3419,"line":10674},[3417,15179,15180],{"class":3430},"        min_value(",[3417,15182,15183],{"class":3441},"0.01",[3417,15185,10581],{"class":3430},[3417,15187,15188,15191,15194],{"class":3419,"line":10680},[3417,15189,15190],{"class":3430},"        max_value(",[3417,15192,15193],{"class":3441},"1_000_000",[3417,15195,10581],{"class":3430},[3417,15197,15198],{"class":3419,"line":10704},[3417,15199,15158],{"class":3430},[3417,15201,15203],{"class":3419,"line":15202},97,[3417,15204,15205],{"class":3430},"    sku = ValidatedField(\n",[3417,15207,15209,15211,15213],{"class":3419,"line":15208},98,[3417,15210,15144],{"class":3430},[3417,15212,3876],{"class":3722},[3417,15214,10581],{"class":3430},[3417,15216,15218,15221,15223,15227,15231,15234,15237,15241,15244,15247,15250],{"class":3419,"line":15217},99,[3417,15219,15220],{"class":3430},"        regex_match(",[3417,15222,11621],{"class":3718},[3417,15224,15226],{"class":15225},"shsrj","\"^",[3417,15228,15230],{"class":15229},"sAV2Q","[",[3417,15232,15233],{"class":15225},"A-Z",[3417,15235,15236],{"class":15229},"]",[3417,15238,15240],{"class":15239},"sYsli","{2,4}",[3417,15242,15243],{"class":15225},"-\\d",[3417,15245,15246],{"class":15239},"{4,8}",[3417,15248,15249],{"class":15225},"$\"",[3417,15251,10581],{"class":3430},[3417,15253,15255],{"class":3419,"line":15254},100,[3417,15256,15158],{"class":3430},[3417,15258,15260],{"class":3419,"line":15259},101,[3417,15261,15262],{"class":3430},"    stock = ValidatedField(\n",[3417,15264,15266,15268,15270],{"class":3419,"line":15265},102,[3417,15267,15144],{"class":3430},[3417,15269,6030],{"class":3722},[3417,15271,10581],{"class":3430},[3417,15273,15275,15277,15279],{"class":3419,"line":15274},103,[3417,15276,15180],{"class":3430},[3417,15278,3967],{"class":3441},[3417,15280,10581],{"class":3430},[3417,15282,15284,15287,15289,15291],{"class":3419,"line":15283},104,[3417,15285,15286],{"class":3741},"        default",[3417,15288,7334],{"class":3430},[3417,15290,3967],{"class":3441},[3417,15292,13054],{"class":3430},[3417,15294,15296],{"class":3419,"line":15295},105,[3417,15297,15158],{"class":3430},[3417,15299,15301],{"class":3419,"line":15300},106,[3417,15302,3452],{"emptyLinePlaceholder":3451},[3417,15304,15306,15308,15310,15312,15314,15316,15318,15320,15322,15324,15327,15329,15331,15333,15336,15338,15340,15342,15345,15347,15349,15351,15353],{"class":3419,"line":15305},107,[3417,15307,3731],{"class":3718},[3417,15309,3735],{"class":3734},[3417,15311,3738],{"class":3430},[3417,15313,3742],{"class":3741},[3417,15315,3438],{"class":3430},[3417,15317,9422],{"class":3741},[3417,15319,3750],{"class":3430},[3417,15321,3876],{"class":3722},[3417,15323,3438],{"class":3430},[3417,15325,15326],{"class":3741},"price",[3417,15328,3750],{"class":3430},[3417,15330,3536],{"class":3722},[3417,15332,3438],{"class":3430},[3417,15334,15335],{"class":3741},"sku",[3417,15337,3750],{"class":3430},[3417,15339,3876],{"class":3722},[3417,15341,3438],{"class":3430},[3417,15343,15344],{"class":3741},"stock",[3417,15346,3750],{"class":3430},[3417,15348,6030],{"class":3722},[3417,15350,10180],{"class":3430},[3417,15352,3967],{"class":3441},[3417,15354,3764],{"class":3430},[3417,15356,15358,15360],{"class":3419,"line":15357},108,[3417,15359,3769],{"class":3718},[3417,15361,15362],{"class":3430},".name  = name\n",[3417,15364,15366,15368],{"class":3419,"line":15365},109,[3417,15367,3769],{"class":3718},[3417,15369,15370],{"class":3430},".price = price\n",[3417,15372,15374,15376],{"class":3419,"line":15373},110,[3417,15375,3769],{"class":3718},[3417,15377,15378],{"class":3430},".sku   = sku\n",[3417,15380,15382,15384],{"class":3419,"line":15381},111,[3417,15383,3769],{"class":3718},[3417,15385,15386],{"class":3430},".stock = stock\n",[3417,15388,15390],{"class":3419,"line":15389},112,[3417,15391,3452],{"emptyLinePlaceholder":3451},[3417,15393,15395,15397,15399,15401,15403,15405,15407],{"class":3419,"line":15394},113,[3417,15396,3731],{"class":3718},[3417,15398,9979],{"class":3734},[3417,15400,3738],{"class":3430},[3417,15402,3742],{"class":3741},[3417,15404,3950],{"class":3430},[3417,15406,3876],{"class":3722},[3417,15408,3726],{"class":3430},[3417,15410,15412,15414,15416,15419,15421,15423,15425,15428,15430,15433,15435,15438,15440,15443,15445],{"class":3419,"line":15411},114,[3417,15413,4130],{"class":3960},[3417,15415,11468],{"class":3718},[3417,15417,15418],{"class":3434},"\"Product(",[3417,15420,4744],{"class":3718},[3417,15422,10260],{"class":3430},[3417,15424,9861],{"class":3718},[3417,15426,15427],{"class":3434},", price=",[3417,15429,4744],{"class":3718},[3417,15431,15432],{"class":3430},".price",[3417,15434,4012],{"class":3718},[3417,15436,15437],{"class":3434},", sku=",[3417,15439,4744],{"class":3718},[3417,15441,15442],{"class":3430},".sku",[3417,15444,9861],{"class":3718},[3417,15446,10313],{"class":3434},[3417,15448,15450],{"class":3419,"line":15449},115,[3417,15451,3452],{"emptyLinePlaceholder":3451},[3417,15453,15455],{"class":3419,"line":15454},116,[3417,15456,3452],{"emptyLinePlaceholder":3451},[3417,15458,15460],{"class":3419,"line":15459},117,[3417,15461,11503],{"class":3423},[3417,15463,15465,15467,15469,15472],{"class":3419,"line":15464},118,[3417,15466,4170],{"class":3734},[3417,15468,3738],{"class":3430},[3417,15470,15471],{"class":3434},"\"=== Валідний продукт ===\"",[3417,15473,3445],{"class":3430},[3417,15475,15477,15480,15483,15485,15488,15490,15493,15495,15497,15499,15501],{"class":3419,"line":15476},119,[3417,15478,15479],{"class":3430},"p = Product(",[3417,15481,15482],{"class":3434},"\"MacBook Pro\"",[3417,15484,3438],{"class":3430},[3417,15486,15487],{"class":3441},"89999.0",[3417,15489,3438],{"class":3430},[3417,15491,15492],{"class":3434},"\"MBP-20242\"",[3417,15494,3438],{"class":3430},[3417,15496,15344],{"class":3741},[3417,15498,7334],{"class":3430},[3417,15500,6177],{"class":3441},[3417,15502,3445],{"class":3430},[3417,15504,15506,15508],{"class":3419,"line":15505},120,[3417,15507,4170],{"class":3734},[3417,15509,15510],{"class":3430},"(p)\n",[3417,15512,15514,15516,15518,15520,15523,15525,15528,15530,15532],{"class":3419,"line":15513},121,[3417,15515,4170],{"class":3734},[3417,15517,3738],{"class":3430},[3417,15519,4001],{"class":3718},[3417,15521,15522],{"class":3434},"\"stock (default worked): ",[3417,15524,4007],{"class":3718},[3417,15526,15527],{"class":3430},"p.stock",[3417,15529,4012],{"class":3718},[3417,15531,4015],{"class":3434},[3417,15533,3445],{"class":3430},[3417,15535,15537],{"class":3419,"line":15536},122,[3417,15538,3452],{"emptyLinePlaceholder":3451},[3417,15540,15542,15544,15546,15548,15550,15553],{"class":3419,"line":15541},123,[3417,15543,4170],{"class":3734},[3417,15545,3738],{"class":3430},[3417,15547,4015],{"class":3434},[3417,15549,6476],{"class":6475},[3417,15551,15552],{"class":3434},"=== Тести валідації ===\"",[3417,15554,3445],{"class":3430},[3417,15556,15558],{"class":3419,"line":15557},124,[3417,15559,15560],{"class":3430},"errors_tests = [\n",[3417,15562,15564,15566,15568,15570,15572,15575,15578,15580,15582,15584,15587],{"class":3419,"line":15563},125,[3417,15565,10553],{"class":3430},[3417,15567,10556],{"class":3718},[3417,15569,3750],{"class":3430},[3417,15571,10561],{"class":3734},[3417,15573,15574],{"class":3430},"(p, ",[3417,15576,15577],{"class":3434},"'price'",[3417,15579,10600],{"class":3430},[3417,15581,10099],{"class":3441},[3417,15583,13847],{"class":3430},[3417,15585,15586],{"class":3434},"\"price=-1\"",[3417,15588,10581],{"class":3430},[3417,15590,15592,15594,15596,15598,15600,15602,15604,15606,15609,15611,15614],{"class":3419,"line":15591},126,[3417,15593,10553],{"class":3430},[3417,15595,10556],{"class":3718},[3417,15597,3750],{"class":3430},[3417,15599,10561],{"class":3734},[3417,15601,15574],{"class":3430},[3417,15603,15577],{"class":3434},[3417,15605,3438],{"class":3430},[3417,15607,15608],{"class":3441},"2_000_000",[3417,15610,13875],{"class":3430},[3417,15612,15613],{"class":3434},"\"price=2M (>max)\"",[3417,15615,10581],{"class":3430},[3417,15617,15619,15621,15623,15625,15627,15629,15632,15634,15637,15639,15642],{"class":3419,"line":15618},127,[3417,15620,10553],{"class":3430},[3417,15622,10556],{"class":3718},[3417,15624,3750],{"class":3430},[3417,15626,10561],{"class":3734},[3417,15628,15574],{"class":3430},[3417,15630,15631],{"class":3434},"'sku'",[3417,15633,3438],{"class":3430},[3417,15635,15636],{"class":3434},"\"invalid\"",[3417,15638,10634],{"class":3430},[3417,15640,15641],{"class":3434},"\"sku='invalid'\"",[3417,15643,10581],{"class":3430},[3417,15645,15647,15649,15651,15653,15655,15657,15660,15662,15664,15666,15669],{"class":3419,"line":15646},128,[3417,15648,10553],{"class":3430},[3417,15650,10556],{"class":3718},[3417,15652,3750],{"class":3430},[3417,15654,10561],{"class":3734},[3417,15656,15574],{"class":3430},[3417,15658,15659],{"class":3434},"'stock'",[3417,15661,10600],{"class":3430},[3417,15663,6177],{"class":3441},[3417,15665,13847],{"class":3430},[3417,15667,15668],{"class":3434},"\"stock=-5\"",[3417,15670,10581],{"class":3430},[3417,15672,15674,15676,15678,15680,15682,15684,15687,15689,15691,15694,15697],{"class":3419,"line":15673},129,[3417,15675,10553],{"class":3430},[3417,15677,10556],{"class":3718},[3417,15679,3750],{"class":3430},[3417,15681,10561],{"class":3734},[3417,15683,15574],{"class":3430},[3417,15685,15686],{"class":3434},"'name'",[3417,15688,3438],{"class":3430},[3417,15690,12016],{"class":3434},[3417,15692,15693],{"class":3430},"),       ",[3417,15695,15696],{"class":3434},"\"name=''\"",[3417,15698,10581],{"class":3430},[3417,15700,15702],{"class":3419,"line":15701},130,[3417,15703,10645],{"class":3430},[3417,15705,15707,15709,15711,15713],{"class":3419,"line":15706},131,[3417,15708,10437],{"class":3960},[3417,15710,10658],{"class":3430},[3417,15712,10443],{"class":3960},[3417,15714,15715],{"class":3430}," errors_tests:\n",[3417,15717,15719,15721],{"class":3419,"line":15718},132,[3417,15720,10669],{"class":3960},[3417,15722,3726],{"class":3430},[3417,15724,15726],{"class":3419,"line":15725},133,[3417,15727,10677],{"class":3430},[3417,15729,15731,15733,15735,15737],{"class":3419,"line":15730},134,[3417,15732,10683],{"class":3960},[3417,15734,3977],{"class":3722},[3417,15736,4494],{"class":3960},[3417,15738,4497],{"class":3430},[3417,15740,15742,15744,15746,15748,15750,15752,15754,15756,15758,15760,15763,15765,15767,15769],{"class":3419,"line":15741},135,[3417,15743,4734],{"class":3734},[3417,15745,3738],{"class":3430},[3417,15747,4001],{"class":3718},[3417,15749,10713],{"class":3434},[3417,15751,4007],{"class":3718},[3417,15753,10718],{"class":3430},[3417,15755,4012],{"class":3718},[3417,15757,15236],{"class":3434},[3417,15759,6476],{"class":6475},[3417,15761,15762],{"class":3718},"  {",[3417,15764,4513],{"class":3430},[3417,15766,4012],{"class":3718},[3417,15768,4015],{"class":3434},[3417,15770,3445],{"class":3430},[4189,15772,15774,15782,15786,15801,15808,15811,15815,15819,15826,15830,15837,15841,15848,15852,15859,15863],{"title":15773},"python validated_field.py",[4193,15775,15777,4201,15780],{"className":15776},[3419],[3417,15778,4200],{"className":15779},[4199],[3516,15781,15773],{},[4193,15783,15785],{"className":15784},[3419],"=== Валідний продукт ===",[4193,15787,15789,15790,15427,15794,15437,15797,10775],{"className":15788},[3419],"Product(",[3417,15791,15793],{"className":15792},[4607],"'MacBook Pro'",[3417,15795,15487],{"className":15796},[4607],[3417,15798,15800],{"className":15799},[4607],"'MBP-20242'",[4193,15802,15804,15805],{"className":15803},[3419],"stock (default worked): ",[3417,15806,6177],{"className":15807},[4607],[4193,15809],{"className":15810},[3419],[4193,15812,15814],{"className":15813},[3419],"=== Тести валідації ===",[4193,15816,15818],{"className":15817},[3419],"[price=-1]",[4193,15820,4625,15822],{"className":15821},[3419],[3417,15823,15825],{"className":15824},[4598],"Поле 'price': Значення -1 менше мінімуму 0.01",[4193,15827,15829],{"className":15828},[3419],"[price=2M (>max)]",[4193,15831,4625,15833],{"className":15832},[3419],[3417,15834,15836],{"className":15835},[4598],"Поле 'price': Значення 2000000 перевищує максимум 1000000",[4193,15838,15840],{"className":15839},[3419],"[sku='invalid']",[4193,15842,4625,15844],{"className":15843},[3419],[3417,15845,15847],{"className":15846},[4598],"Поле 'sku': Значення 'invalid' не відповідає шаблону '^[A-Z]{2,4}-\\d{4,8}$'",[4193,15849,15851],{"className":15850},[3419],"[stock=-5]",[4193,15853,4625,15855],{"className":15854},[3419],[3417,15856,15858],{"className":15857},[4598],"Поле 'stock': Значення -5 менше мінімуму 0",[4193,15860,15862],{"className":15861},[3419],"[name='']",[4193,15864,4625,15866],{"className":15865},[3419],[3417,15867,15869],{"className":15868},[4598],"Поле 'name': Рядок не може складатися лише з пробілів",[3553,15871],{},[3390,15873,15875],{"id":15874},"практична-лабораторія-e-commerce-кошик-від-а-до-я","Практична лабораторія: E-Commerce кошик від А до Я",[3395,15877,15878,15879,15882],{},"Це комплексний приклад, що поєднує ",[3516,15880,15881],{},"всі концепції статті"," в одній системі:",[4972,15884,15885,15895],{},[4975,15886,15887],{},[4978,15888,15889,15892],{},[4981,15890,15891],{},"Концепція",[4981,15893,15894],{},"Де застосовано",[4997,15896,15897,15912,15925,15944,15960,15973,15987],{},[4978,15898,15899,15903],{},[5002,15900,15901],{},[3399,15902,3821],{},[5002,15904,15905,3438,15908,15911],{},[3399,15906,15907],{},"Product._stock",[3399,15909,15910],{},"Cart._items"," — внутрішні деталі",[4978,15913,15914,15919],{},[5002,15915,15916,15918],{},[3399,15917,4267],{}," (Name Mangling)",[5002,15920,15921,15924],{},[3399,15922,15923],{},"Cart.__discount_code"," — захист від колізій у підкласах",[4978,15926,15927,15932],{},[5002,15928,15929,15931],{},[3399,15930,5086],{}," (геттер)",[5002,15933,15934,3438,15937,3438,15940,15943],{},[3399,15935,15936],{},"Cart.total",[3399,15938,15939],{},"Cart.tax",[3399,15941,15942],{},"Cart.final_price"," — обчислювальні",[4978,15945,15946,15951],{},[5002,15947,15948,15950],{},[3399,15949,5086],{}," (сеттер + валідація)",[5002,15952,15953,3438,15956,15959],{},[3399,15954,15955],{},"Product.price",[3399,15957,15958],{},"Product.stock"," — захищений запис",[4978,15961,15962,15967],{},[5002,15963,15964,15966],{},[3399,15965,5086],{}," (делітер)",[5002,15968,15969,15972],{},[3399,15970,15971],{},"Cart.coupon"," — очищення знижки",[4978,15974,15975,15978],{},[5002,15976,15977],{},"Кастомний дескриптор",[5002,15979,15980,3438,15983,15986],{},[3399,15981,15982],{},"PositiveFloat",[3399,15984,15985],{},"NonNegativeInt"," — перевикористання валідації",[4978,15988,15989,15992],{},[5002,15990,15991],{},"Name Mangling при наслідуванні",[5002,15993,15994,7768,15997,16000],{},[3399,15995,15996],{},"PremiumCart",[3399,15998,15999],{},"__discount_code"," не конфліктує",[3560,16002,16004],{"id":16003},"архітектура-системи","Архітектура системи",[3591,16006,16007],{},[3408,16008,16010],{"className":3595,"code":16009,"language":3597,"meta":3413,"style":3413},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam ArrowColor #6366f1\nskinparam class {\n    BackgroundColor #f3f4f6\n    BorderColor #d1d5db\n}\n\nclass PositiveFloat {\n    + __set_name__(owner, name)\n    + __get__(instance, owner)\n    + __set__(instance, value)\n}\n\nclass NonNegativeInt {\n    + __set_name__(owner, name)\n    + __get__(instance, owner)\n    + __set__(instance, value)\n}\n\nclass Product {\n    + name : str\n    + price : float \u003C\u003CPositiveFloat>>\n    + stock : int \u003C\u003CNonNegativeInt>>\n    - _category : str\n    + is_available : bool \u003C\u003Cproperty>>\n    + reserve(qty) : void\n    + release(qty) : void\n}\n\nclass Cart {\n    - _items : dict\n    - __discount_code : str | None\n    - __discount_percent : float\n    + subtotal : float \u003C\u003Cproperty>>\n    + tax : float \u003C\u003Cproperty>>\n    + discount_amount : float \u003C\u003Cproperty>>\n    + total : float \u003C\u003Cproperty>>\n    + coupon : str | None \u003C\u003Cproperty>>\n    + add(product, qty) : void\n    + remove(product) : void\n    + checkout() : Receipt\n}\n\nclass PremiumCart {\n    - __discount_code : str | None\n    + loyalty_discount : float \u003C\u003Cproperty>>\n    + total : float \u003C\u003Cproperty, override>>\n}\n\nclass Receipt {\n    + items : list\n    + subtotal : float\n    + tax : float\n    + discount : float\n    + total : float\n    + to_text() : str\n}\n\nProduct \"1\" --o \"0..*\" Cart : contains\nCart \u003C|-- PremiumCart\nCart --> Receipt : creates\nPositiveFloat ..> Product : validates price\nNonNegativeInt ..> Product : validates stock\n@enduml\n",[3399,16011,16012,16016,16020,16024,16028,16033,16038,16043,16047,16051,16056,16061,16066,16071,16075,16079,16084,16088,16092,16096,16100,16104,16109,16114,16119,16124,16129,16134,16139,16144,16148,16152,16157,16162,16167,16172,16177,16182,16187,16192,16197,16202,16207,16212,16216,16220,16225,16229,16234,16239,16243,16247,16252,16257,16262,16267,16272,16277,16282,16286,16290,16295,16300,16305,16310,16315],{"__ignoreMap":3413},[3417,16013,16014],{"class":3419,"line":3420},[3417,16015,3604],{},[3417,16017,16018],{"class":3419,"line":3427},[3417,16019,3609],{},[3417,16021,16022],{"class":3419,"line":3448},[3417,16023,3614],{},[3417,16025,16026],{"class":3419,"line":3455},[3417,16027,3619],{},[3417,16029,16030],{"class":3419,"line":3461},[3417,16031,16032],{},"skinparam class {\n",[3417,16034,16035],{"class":3419,"line":3473},[3417,16036,16037],{},"    BackgroundColor #f3f4f6\n",[3417,16039,16040],{"class":3419,"line":3485},[3417,16041,16042],{},"    BorderColor #d1d5db\n",[3417,16044,16045],{"class":3419,"line":3490},[3417,16046,3654],{},[3417,16048,16049],{"class":3419,"line":3496},[3417,16050,3452],{"emptyLinePlaceholder":3451},[3417,16052,16053],{"class":3419,"line":3645},[3417,16054,16055],{},"class PositiveFloat {\n",[3417,16057,16058],{"class":3419,"line":3651},[3417,16059,16060],{},"    + __set_name__(owner, name)\n",[3417,16062,16063],{"class":3419,"line":3657},[3417,16064,16065],{},"    + __get__(instance, owner)\n",[3417,16067,16068],{"class":3419,"line":3662},[3417,16069,16070],{},"    + __set__(instance, value)\n",[3417,16072,16073],{"class":3419,"line":3668},[3417,16074,3654],{},[3417,16076,16077],{"class":3419,"line":3673},[3417,16078,3452],{"emptyLinePlaceholder":3451},[3417,16080,16081],{"class":3419,"line":3679},[3417,16082,16083],{},"class NonNegativeInt {\n",[3417,16085,16086],{"class":3419,"line":3685},[3417,16087,16060],{},[3417,16089,16090],{"class":3419,"line":4074},[3417,16091,16065],{},[3417,16093,16094],{"class":3419,"line":4082},[3417,16095,16070],{},[3417,16097,16098],{"class":3419,"line":4104},[3417,16099,3654],{},[3417,16101,16102],{"class":3419,"line":4109},[3417,16103,3452],{"emptyLinePlaceholder":3451},[3417,16105,16106],{"class":3419,"line":4127},[3417,16107,16108],{},"class Product {\n",[3417,16110,16111],{"class":3419,"line":4522},[3417,16112,16113],{},"    + name : str\n",[3417,16115,16116],{"class":3419,"line":4527},[3417,16117,16118],{},"    + price : float \u003C\u003CPositiveFloat>>\n",[3417,16120,16121],{"class":3419,"line":4533},[3417,16122,16123],{},"    + stock : int \u003C\u003CNonNegativeInt>>\n",[3417,16125,16126],{"class":3419,"line":4546},[3417,16127,16128],{},"    - _category : str\n",[3417,16130,16131],{"class":3419,"line":4551},[3417,16132,16133],{},"    + is_available : bool \u003C\u003Cproperty>>\n",[3417,16135,16136],{"class":3419,"line":4557},[3417,16137,16138],{},"    + reserve(qty) : void\n",[3417,16140,16141],{"class":3419,"line":4565},[3417,16142,16143],{},"    + release(qty) : void\n",[3417,16145,16146],{"class":3419,"line":4574},[3417,16147,3654],{},[3417,16149,16150],{"class":3419,"line":5657},[3417,16151,3452],{"emptyLinePlaceholder":3451},[3417,16153,16154],{"class":3419,"line":5680},[3417,16155,16156],{},"class Cart {\n",[3417,16158,16159],{"class":3419,"line":5685},[3417,16160,16161],{},"    - _items : dict\n",[3417,16163,16164],{"class":3419,"line":5691},[3417,16165,16166],{},"    - __discount_code : str | None\n",[3417,16168,16169],{"class":3419,"line":5700},[3417,16170,16171],{},"    - __discount_percent : float\n",[3417,16173,16174],{"class":3419,"line":5722},[3417,16175,16176],{},"    + subtotal : float \u003C\u003Cproperty>>\n",[3417,16178,16179],{"class":3419,"line":5727},[3417,16180,16181],{},"    + tax : float \u003C\u003Cproperty>>\n",[3417,16183,16184],{"class":3419,"line":5733},[3417,16185,16186],{},"    + discount_amount : float \u003C\u003Cproperty>>\n",[3417,16188,16189],{"class":3419,"line":5740},[3417,16190,16191],{},"    + total : float \u003C\u003Cproperty>>\n",[3417,16193,16194],{"class":3419,"line":5749},[3417,16195,16196],{},"    + coupon : str | None \u003C\u003Cproperty>>\n",[3417,16198,16199],{"class":3419,"line":5760},[3417,16200,16201],{},"    + add(product, qty) : void\n",[3417,16203,16204],{"class":3419,"line":6271},[3417,16205,16206],{},"    + remove(product) : void\n",[3417,16208,16209],{"class":3419,"line":6277},[3417,16210,16211],{},"    + checkout() : Receipt\n",[3417,16213,16214],{"class":3419,"line":6289},[3417,16215,3654],{},[3417,16217,16218],{"class":3419,"line":6299},[3417,16219,3452],{"emptyLinePlaceholder":3451},[3417,16221,16222],{"class":3419,"line":6304},[3417,16223,16224],{},"class PremiumCart {\n",[3417,16226,16227],{"class":3419,"line":6310},[3417,16228,16166],{},[3417,16230,16231],{"class":3419,"line":6315},[3417,16232,16233],{},"    + loyalty_discount : float \u003C\u003Cproperty>>\n",[3417,16235,16236],{"class":3419,"line":6322},[3417,16237,16238],{},"    + total : float \u003C\u003Cproperty, override>>\n",[3417,16240,16241],{"class":3419,"line":6340},[3417,16242,3654],{},[3417,16244,16245],{"class":3419,"line":6346},[3417,16246,3452],{"emptyLinePlaceholder":3451},[3417,16248,16249],{"class":3419,"line":8484},[3417,16250,16251],{},"class Receipt {\n",[3417,16253,16254],{"class":3419,"line":8509},[3417,16255,16256],{},"    + items : list\n",[3417,16258,16259],{"class":3419,"line":8520},[3417,16260,16261],{},"    + subtotal : float\n",[3417,16263,16264],{"class":3419,"line":8534},[3417,16265,16266],{},"    + tax : float\n",[3417,16268,16269],{"class":3419,"line":8542},[3417,16270,16271],{},"    + discount : float\n",[3417,16273,16274],{"class":3419,"line":8547},[3417,16275,16276],{},"    + total : float\n",[3417,16278,16279],{"class":3419,"line":8552},[3417,16280,16281],{},"    + to_text() : str\n",[3417,16283,16284],{"class":3419,"line":8563},[3417,16285,3654],{},[3417,16287,16288],{"class":3419,"line":8574},[3417,16289,3452],{"emptyLinePlaceholder":3451},[3417,16291,16292],{"class":3419,"line":8583},[3417,16293,16294],{},"Product \"1\" --o \"0..*\" Cart : contains\n",[3417,16296,16297],{"class":3419,"line":8593},[3417,16298,16299],{},"Cart \u003C|-- PremiumCart\n",[3417,16301,16302],{"class":3419,"line":8598},[3417,16303,16304],{},"Cart --> Receipt : creates\n",[3417,16306,16307],{"class":3419,"line":8605},[3417,16308,16309],{},"PositiveFloat ..> Product : validates price\n",[3417,16311,16312],{"class":3419,"line":8613},[3417,16313,16314],{},"NonNegativeInt ..> Product : validates stock\n",[3417,16316,16317],{"class":3419,"line":8624},[3417,16318,3688],{},[3560,16320,16322],{"id":16321},"реалізація","Реалізація",[11869,16324,16325,16901,17568,19964],{},[3408,16326,16329],{"className":3410,"code":16327,"filename":16328,"language":3412,"meta":3413,"style":3413},"# Перевикористовувані дескриптори для числової валідації\n\nclass PositiveFloat:\n    \"\"\"Дескриптор: додатне дійсне число (> 0).\"\"\"\n\n    def __set_name__(self, owner: type, name: str) -> None:\n        self.public_name  = name\n        self.private_name = f\"_{name}\"\n\n    def __get__(self, instance, owner):\n        if instance is None:\n            return self\n        return getattr(instance, self.private_name)\n\n    def __set__(self, instance, value) -> None:\n        if not isinstance(value, (int, float)):\n            raise TypeError(\n                f\"'{self.public_name}' має бути числом, \"\n                f\"отримано {type(value).__name__!r}\"\n            )\n        if value \u003C= 0:\n            raise ValueError(\n                f\"'{self.public_name}' має бути > 0, отримано {value}\"\n            )\n        setattr(instance, self.private_name, float(value))\n\n\nclass NonNegativeInt:\n    \"\"\"Дескриптор: ціле невід'ємне число (>= 0).\"\"\"\n\n    def __set_name__(self, owner: type, name: str) -> None:\n        self.public_name  = name\n        self.private_name = f\"_{name}\"\n\n    def __get__(self, instance, owner):\n        if instance is None:\n            return self\n        return getattr(instance, self.private_name, 0)\n\n    def __set__(self, instance, value) -> None:\n        if not isinstance(value, int):\n            raise TypeError(\n                f\"'{self.public_name}' має бути int, \"\n                f\"отримано {type(value).__name__!r}\"\n            )\n        if value \u003C 0:\n            raise ValueError(\n                f\"'{self.public_name}' має бути >= 0, отримано {value}\"\n            )\n        setattr(instance, self.private_name, value)\n","descriptors.py",[3399,16330,16331,16336,16340,16349,16354,16358,16390,16396,16414,16418,16438,16450,16456,16469,16473,16497,16515,16523,16538,16556,16560,16570,16578,16601,16605,16620,16624,16628,16637,16642,16646,16678,16684,16702,16706,16726,16738,16744,16760,16764,16788,16802,16810,16824,16842,16846,16856,16864,16887,16891],{"__ignoreMap":3413},[3417,16332,16333],{"class":3419,"line":3420},[3417,16334,16335],{"class":3423},"# Перевикористовувані дескриптори для числової валідації\n",[3417,16337,16338],{"class":3419,"line":3427},[3417,16339,3452],{"emptyLinePlaceholder":3451},[3417,16341,16342,16344,16347],{"class":3419,"line":3448},[3417,16343,3719],{"class":3718},[3417,16345,16346],{"class":3722}," PositiveFloat",[3417,16348,3726],{"class":3430},[3417,16350,16351],{"class":3419,"line":3455},[3417,16352,16353],{"class":3434},"    \"\"\"Дескриптор: додатне дійсне число (> 0).\"\"\"\n",[3417,16355,16356],{"class":3419,"line":3461},[3417,16357,3452],{"emptyLinePlaceholder":3451},[3417,16359,16360,16362,16364,16366,16368,16370,16372,16374,16376,16378,16380,16382,16384,16386,16388],{"class":3419,"line":3473},[3417,16361,3731],{"class":3718},[3417,16363,9409],{"class":3734},[3417,16365,3738],{"class":3430},[3417,16367,3742],{"class":3741},[3417,16369,3438],{"class":3430},[3417,16371,3871],{"class":3741},[3417,16373,3750],{"class":3430},[3417,16375,6056],{"class":3722},[3417,16377,3438],{"class":3430},[3417,16379,9422],{"class":3741},[3417,16381,3750],{"class":3430},[3417,16383,3876],{"class":3722},[3417,16385,3950],{"class":3430},[3417,16387,3953],{"class":3718},[3417,16389,3726],{"class":3430},[3417,16391,16392,16394],{"class":3419,"line":3485},[3417,16393,3769],{"class":3718},[3417,16395,9677],{"class":3430},[3417,16397,16398,16400,16402,16404,16406,16408,16410,16412],{"class":3419,"line":3490},[3417,16399,3769],{"class":3718},[3417,16401,9451],{"class":3430},[3417,16403,4001],{"class":3718},[3417,16405,9456],{"class":3434},[3417,16407,4007],{"class":3718},[3417,16409,9422],{"class":3430},[3417,16411,4012],{"class":3718},[3417,16413,9696],{"class":3434},[3417,16415,16416],{"class":3419,"line":3496},[3417,16417,3452],{"emptyLinePlaceholder":3451},[3417,16419,16420,16422,16424,16426,16428,16430,16432,16434,16436],{"class":3419,"line":3645},[3417,16421,3731],{"class":3718},[3417,16423,8086],{"class":3734},[3417,16425,3738],{"class":3430},[3417,16427,3742],{"class":3741},[3417,16429,3438],{"class":3430},[3417,16431,7441],{"class":3741},[3417,16433,3438],{"class":3430},[3417,16435,3871],{"class":3741},[3417,16437,3764],{"class":3430},[3417,16439,16440,16442,16444,16446,16448],{"class":3419,"line":3651},[3417,16441,3961],{"class":3960},[3417,16443,8112],{"class":3430},[3417,16445,8115],{"class":3718},[3417,16447,8073],{"class":3718},[3417,16449,3726],{"class":3430},[3417,16451,16452,16454],{"class":3419,"line":3657},[3417,16453,8124],{"class":3960},[3417,16455,8127],{"class":3718},[3417,16457,16458,16460,16462,16464,16466],{"class":3419,"line":3662},[3417,16459,4130],{"class":3960},[3417,16461,9752],{"class":3734},[3417,16463,9755],{"class":3430},[3417,16465,3742],{"class":3718},[3417,16467,16468],{"class":3430},".private_name)\n",[3417,16470,16471],{"class":3419,"line":3668},[3417,16472,3452],{"emptyLinePlaceholder":3451},[3417,16474,16475,16477,16479,16481,16483,16485,16487,16489,16491,16493,16495],{"class":3419,"line":3673},[3417,16476,3731],{"class":3718},[3417,16478,8175],{"class":3734},[3417,16480,3738],{"class":3430},[3417,16482,3742],{"class":3741},[3417,16484,3438],{"class":3430},[3417,16486,7441],{"class":3741},[3417,16488,3438],{"class":3430},[3417,16490,5200],{"class":3741},[3417,16492,3950],{"class":3430},[3417,16494,3953],{"class":3718},[3417,16496,3726],{"class":3430},[3417,16498,16499,16501,16503,16505,16507,16509,16511,16513],{"class":3419,"line":3679},[3417,16500,3961],{"class":3960},[3417,16502,6021],{"class":3718},[3417,16504,6024],{"class":3734},[3417,16506,6027],{"class":3430},[3417,16508,6030],{"class":3722},[3417,16510,3438],{"class":3430},[3417,16512,3536],{"class":3722},[3417,16514,6037],{"class":3430},[3417,16516,16517,16519,16521],{"class":3419,"line":3685},[3417,16518,3974],{"class":3960},[3417,16520,6044],{"class":3722},[3417,16522,9825],{"class":3430},[3417,16524,16525,16527,16529,16531,16533,16535],{"class":3419,"line":4074},[3417,16526,9830],{"class":3718},[3417,16528,9833],{"class":3434},[3417,16530,4744],{"class":3718},[3417,16532,9838],{"class":3430},[3417,16534,4012],{"class":3718},[3417,16536,16537],{"class":3434},"' має бути числом, \"\n",[3417,16539,16540,16542,16544,16546,16548,16550,16552,16554],{"class":3419,"line":4082},[3417,16541,9830],{"class":3718},[3417,16543,9850],{"class":3434},[3417,16545,4007],{"class":3718},[3417,16547,6056],{"class":3722},[3417,16549,6059],{"class":3430},[3417,16551,6062],{"class":3741},[3417,16553,9861],{"class":3718},[3417,16555,9696],{"class":3434},[3417,16557,16558],{"class":3419,"line":4104},[3417,16559,9868],{"class":3430},[3417,16561,16562,16564,16566,16568],{"class":3419,"line":4109},[3417,16563,3961],{"class":3960},[3417,16565,11104],{"class":3430},[3417,16567,3967],{"class":3441},[3417,16569,3726],{"class":3430},[3417,16571,16572,16574,16576],{"class":3419,"line":4127},[3417,16573,3974],{"class":3960},[3417,16575,3977],{"class":3722},[3417,16577,9825],{"class":3430},[3417,16579,16580,16582,16584,16586,16588,16590,16593,16595,16597,16599],{"class":3419,"line":4522},[3417,16581,9830],{"class":3718},[3417,16583,9833],{"class":3434},[3417,16585,4744],{"class":3718},[3417,16587,9838],{"class":3430},[3417,16589,4012],{"class":3718},[3417,16591,16592],{"class":3434},"' має бути > 0, отримано ",[3417,16594,4007],{"class":3718},[3417,16596,5200],{"class":3430},[3417,16598,4012],{"class":3718},[3417,16600,9696],{"class":3434},[3417,16602,16603],{"class":3419,"line":4527},[3417,16604,9868],{"class":3430},[3417,16606,16607,16609,16611,16613,16615,16617],{"class":3419,"line":4533},[3417,16608,9961],{"class":3734},[3417,16610,9755],{"class":3430},[3417,16612,3742],{"class":3718},[3417,16614,9760],{"class":3430},[3417,16616,3536],{"class":3722},[3417,16618,16619],{"class":3430},"(value))\n",[3417,16621,16622],{"class":3419,"line":4546},[3417,16623,3452],{"emptyLinePlaceholder":3451},[3417,16625,16626],{"class":3419,"line":4551},[3417,16627,3452],{"emptyLinePlaceholder":3451},[3417,16629,16630,16632,16635],{"class":3419,"line":4557},[3417,16631,3719],{"class":3718},[3417,16633,16634],{"class":3722}," NonNegativeInt",[3417,16636,3726],{"class":3430},[3417,16638,16639],{"class":3419,"line":4565},[3417,16640,16641],{"class":3434},"    \"\"\"Дескриптор: ціле невід'ємне число (>= 0).\"\"\"\n",[3417,16643,16644],{"class":3419,"line":4574},[3417,16645,3452],{"emptyLinePlaceholder":3451},[3417,16647,16648,16650,16652,16654,16656,16658,16660,16662,16664,16666,16668,16670,16672,16674,16676],{"class":3419,"line":5657},[3417,16649,3731],{"class":3718},[3417,16651,9409],{"class":3734},[3417,16653,3738],{"class":3430},[3417,16655,3742],{"class":3741},[3417,16657,3438],{"class":3430},[3417,16659,3871],{"class":3741},[3417,16661,3750],{"class":3430},[3417,16663,6056],{"class":3722},[3417,16665,3438],{"class":3430},[3417,16667,9422],{"class":3741},[3417,16669,3750],{"class":3430},[3417,16671,3876],{"class":3722},[3417,16673,3950],{"class":3430},[3417,16675,3953],{"class":3718},[3417,16677,3726],{"class":3430},[3417,16679,16680,16682],{"class":3419,"line":5680},[3417,16681,3769],{"class":3718},[3417,16683,9677],{"class":3430},[3417,16685,16686,16688,16690,16692,16694,16696,16698,16700],{"class":3419,"line":5685},[3417,16687,3769],{"class":3718},[3417,16689,9451],{"class":3430},[3417,16691,4001],{"class":3718},[3417,16693,9456],{"class":3434},[3417,16695,4007],{"class":3718},[3417,16697,9422],{"class":3430},[3417,16699,4012],{"class":3718},[3417,16701,9696],{"class":3434},[3417,16703,16704],{"class":3419,"line":5691},[3417,16705,3452],{"emptyLinePlaceholder":3451},[3417,16707,16708,16710,16712,16714,16716,16718,16720,16722,16724],{"class":3419,"line":5700},[3417,16709,3731],{"class":3718},[3417,16711,8086],{"class":3734},[3417,16713,3738],{"class":3430},[3417,16715,3742],{"class":3741},[3417,16717,3438],{"class":3430},[3417,16719,7441],{"class":3741},[3417,16721,3438],{"class":3430},[3417,16723,3871],{"class":3741},[3417,16725,3764],{"class":3430},[3417,16727,16728,16730,16732,16734,16736],{"class":3419,"line":5722},[3417,16729,3961],{"class":3960},[3417,16731,8112],{"class":3430},[3417,16733,8115],{"class":3718},[3417,16735,8073],{"class":3718},[3417,16737,3726],{"class":3430},[3417,16739,16740,16742],{"class":3419,"line":5727},[3417,16741,8124],{"class":3960},[3417,16743,8127],{"class":3718},[3417,16745,16746,16748,16750,16752,16754,16756,16758],{"class":3419,"line":5733},[3417,16747,4130],{"class":3960},[3417,16749,9752],{"class":3734},[3417,16751,9755],{"class":3430},[3417,16753,3742],{"class":3718},[3417,16755,9760],{"class":3430},[3417,16757,3967],{"class":3441},[3417,16759,3445],{"class":3430},[3417,16761,16762],{"class":3419,"line":5740},[3417,16763,3452],{"emptyLinePlaceholder":3451},[3417,16765,16766,16768,16770,16772,16774,16776,16778,16780,16782,16784,16786],{"class":3419,"line":5749},[3417,16767,3731],{"class":3718},[3417,16769,8175],{"class":3734},[3417,16771,3738],{"class":3430},[3417,16773,3742],{"class":3741},[3417,16775,3438],{"class":3430},[3417,16777,7441],{"class":3741},[3417,16779,3438],{"class":3430},[3417,16781,5200],{"class":3741},[3417,16783,3950],{"class":3430},[3417,16785,3953],{"class":3718},[3417,16787,3726],{"class":3430},[3417,16789,16790,16792,16794,16796,16798,16800],{"class":3419,"line":5760},[3417,16791,3961],{"class":3960},[3417,16793,6021],{"class":3718},[3417,16795,6024],{"class":3734},[3417,16797,9812],{"class":3430},[3417,16799,6030],{"class":3722},[3417,16801,3764],{"class":3430},[3417,16803,16804,16806,16808],{"class":3419,"line":6271},[3417,16805,3974],{"class":3960},[3417,16807,6044],{"class":3722},[3417,16809,9825],{"class":3430},[3417,16811,16812,16814,16816,16818,16820,16822],{"class":3419,"line":6277},[3417,16813,9830],{"class":3718},[3417,16815,9833],{"class":3434},[3417,16817,4744],{"class":3718},[3417,16819,9838],{"class":3430},[3417,16821,4012],{"class":3718},[3417,16823,9843],{"class":3434},[3417,16825,16826,16828,16830,16832,16834,16836,16838,16840],{"class":3419,"line":6289},[3417,16827,9830],{"class":3718},[3417,16829,9850],{"class":3434},[3417,16831,4007],{"class":3718},[3417,16833,6056],{"class":3722},[3417,16835,6059],{"class":3430},[3417,16837,6062],{"class":3741},[3417,16839,9861],{"class":3718},[3417,16841,9696],{"class":3434},[3417,16843,16844],{"class":3419,"line":6299},[3417,16845,9868],{"class":3430},[3417,16847,16848,16850,16852,16854],{"class":3419,"line":6304},[3417,16849,3961],{"class":3960},[3417,16851,5216],{"class":3430},[3417,16853,3967],{"class":3441},[3417,16855,3726],{"class":3430},[3417,16857,16858,16860,16862],{"class":3419,"line":6310},[3417,16859,3974],{"class":3960},[3417,16861,3977],{"class":3722},[3417,16863,9825],{"class":3430},[3417,16865,16866,16868,16870,16872,16874,16876,16879,16881,16883,16885],{"class":3419,"line":6315},[3417,16867,9830],{"class":3718},[3417,16869,9833],{"class":3434},[3417,16871,4744],{"class":3718},[3417,16873,9838],{"class":3430},[3417,16875,4012],{"class":3718},[3417,16877,16878],{"class":3434},"' має бути >= 0, отримано ",[3417,16880,4007],{"class":3718},[3417,16882,5200],{"class":3430},[3417,16884,4012],{"class":3718},[3417,16886,9696],{"class":3434},[3417,16888,16889],{"class":3419,"line":6322},[3417,16890,9868],{"class":3430},[3417,16892,16893,16895,16897,16899],{"class":3419,"line":6340},[3417,16894,9961],{"class":3734},[3417,16896,9755],{"class":3430},[3417,16898,3742],{"class":3718},[3417,16900,9968],{"class":3430},[3408,16902,16905],{"className":3410,"code":16903,"filename":16904,"language":3412,"meta":3413,"style":3413},"from descriptors import PositiveFloat, NonNegativeInt\n\nclass Product:\n    \"\"\"\n    Товар інтернет-магазину.\n\n    Публічний інтерфейс:\n        name, price, stock — доступ через дескриптори\u002Fproperty\n        is_available        — обчислювана read-only властивість\n        reserve(qty)        — резервує stock\n        release(qty)        — повертає stock\n\n    Деталі реалізації (захищені):\n        _category           — категорія (рядок), може змінитися\n    \"\"\"\n\n    # Дескриптори — валідація при кожному присвоєнні\n    price = PositiveFloat()\n    stock = NonNegativeInt()\n\n    def __init__(\n        self,\n        name:     str,\n        price:    float,\n        stock:    int,\n        category: str = \"Загальне\",\n    ):\n        if not name.strip():\n            raise ValueError(\"Назва продукту не може бути порожньою\")\n        self.name      = name.strip()\n        self.price     = price    # → PositiveFloat.__set__ (валідація)\n        self.stock     = stock    # → NonNegativeInt.__set__ (валідація)\n        self._category = category # захищений: деталь реалізації\n\n    # ── Обчислювальні властивості (read-only) ─────────────────────────────────\n\n    @property\n    def is_available(self) -> bool:\n        \"\"\"True, якщо товар є у наявності.\"\"\"\n        return self.stock > 0\n\n    @property\n    def category(self) -> str:\n        \"\"\"Категорія товару (лише читання ззовні).\"\"\"\n        return self._category\n\n    # ── Методи бізнес-логіки ──────────────────────────────────────────────────\n\n    def reserve(self, qty: int) -> None:\n        \"\"\"Резервує qty одиниць товару (зменшує stock).\"\"\"\n        if not isinstance(qty, int) or qty \u003C= 0:\n            raise ValueError(f\"qty має бути додатним int, отримано {qty!r}\")\n        if qty > self.stock:\n            raise ValueError(\n                f\"Недостатньо '{self.name}': потрібно {qty}, \"\n                f\"доступно {self.stock}\"\n            )\n        self.stock -= qty\n\n    def release(self, qty: int) -> None:\n        \"\"\"Повертає qty одиниць товару (збільшує stock).\"\"\"\n        if not isinstance(qty, int) or qty \u003C= 0:\n            raise ValueError(f\"qty має бути додатним int\")\n        self.stock += qty\n\n    def __repr__(self) -> str:\n        status = \"✅\" if self.is_available else \"❌\"\n        return (\n            f\"Product({self.name!r}, price={self.price:.2f} грн, \"\n            f\"stock={self.stock} {status})\"\n        )\n","product.py",[3399,16906,16907,16919,16923,16931,16935,16940,16944,16949,16954,16959,16964,16969,16973,16978,16983,16987,16991,16996,17001,17006,17010,17018,17024,17036,17047,17058,17074,17078,17087,17100,17107,17117,17127,17137,17141,17146,17150,17156,17173,17178,17190,17194,17200,17217,17222,17231,17235,17240,17244,17270,17275,17299,17322,17334,17342,17366,17382,17386,17393,17397,17422,17427,17449,17464,17471,17475,17491,17511,17517,17541,17564],{"__ignoreMap":3413},[3417,16908,16909,16911,16914,16916],{"class":3419,"line":3420},[3417,16910,12959],{"class":3960},[3417,16912,16913],{"class":3430}," descriptors ",[3417,16915,5375],{"class":3960},[3417,16917,16918],{"class":3430}," PositiveFloat, NonNegativeInt\n",[3417,16920,16921],{"class":3419,"line":3427},[3417,16922,3452],{"emptyLinePlaceholder":3451},[3417,16924,16925,16927,16929],{"class":3419,"line":3448},[3417,16926,3719],{"class":3718},[3417,16928,15132],{"class":3722},[3417,16930,3726],{"class":3430},[3417,16932,16933],{"class":3419,"line":3455},[3417,16934,5877],{"class":3434},[3417,16936,16937],{"class":3419,"line":3461},[3417,16938,16939],{"class":3434},"    Товар інтернет-магазину.\n",[3417,16941,16942],{"class":3419,"line":3473},[3417,16943,3452],{"emptyLinePlaceholder":3451},[3417,16945,16946],{"class":3419,"line":3485},[3417,16947,16948],{"class":3434},"    Публічний інтерфейс:\n",[3417,16950,16951],{"class":3419,"line":3490},[3417,16952,16953],{"class":3434},"        name, price, stock — доступ через дескриптори\u002Fproperty\n",[3417,16955,16956],{"class":3419,"line":3496},[3417,16957,16958],{"class":3434},"        is_available        — обчислювана read-only властивість\n",[3417,16960,16961],{"class":3419,"line":3645},[3417,16962,16963],{"class":3434},"        reserve(qty)        — резервує stock\n",[3417,16965,16966],{"class":3419,"line":3651},[3417,16967,16968],{"class":3434},"        release(qty)        — повертає stock\n",[3417,16970,16971],{"class":3419,"line":3657},[3417,16972,3452],{"emptyLinePlaceholder":3451},[3417,16974,16975],{"class":3419,"line":3662},[3417,16976,16977],{"class":3434},"    Деталі реалізації (захищені):\n",[3417,16979,16980],{"class":3419,"line":3668},[3417,16981,16982],{"class":3434},"        _category           — категорія (рядок), може змінитися\n",[3417,16984,16985],{"class":3419,"line":3673},[3417,16986,5877],{"class":3434},[3417,16988,16989],{"class":3419,"line":3679},[3417,16990,3452],{"emptyLinePlaceholder":3451},[3417,16992,16993],{"class":3419,"line":3685},[3417,16994,16995],{"class":3423},"    # Дескриптори — валідація при кожному присвоєнні\n",[3417,16997,16998],{"class":3419,"line":4074},[3417,16999,17000],{"class":3430},"    price = PositiveFloat()\n",[3417,17002,17003],{"class":3419,"line":4082},[3417,17004,17005],{"class":3430},"    stock = NonNegativeInt()\n",[3417,17007,17008],{"class":3419,"line":4104},[3417,17009,3452],{"emptyLinePlaceholder":3451},[3417,17011,17012,17014,17016],{"class":3419,"line":4109},[3417,17013,3731],{"class":3718},[3417,17015,3735],{"class":3734},[3417,17017,9825],{"class":3430},[3417,17019,17020,17022],{"class":3419,"line":4127},[3417,17021,3769],{"class":3741},[3417,17023,13054],{"class":3430},[3417,17025,17026,17029,17032,17034],{"class":3419,"line":4522},[3417,17027,17028],{"class":3741},"        name",[3417,17030,17031],{"class":3430},":     ",[3417,17033,3876],{"class":3722},[3417,17035,13054],{"class":3430},[3417,17037,17038,17041,17043,17045],{"class":3419,"line":4527},[3417,17039,17040],{"class":3741},"        price",[3417,17042,13127],{"class":3430},[3417,17044,3536],{"class":3722},[3417,17046,13054],{"class":3430},[3417,17048,17049,17052,17054,17056],{"class":3419,"line":4533},[3417,17050,17051],{"class":3741},"        stock",[3417,17053,13127],{"class":3430},[3417,17055,6030],{"class":3722},[3417,17057,13054],{"class":3430},[3417,17059,17060,17063,17065,17067,17069,17072],{"class":3419,"line":4546},[3417,17061,17062],{"class":3741},"        category",[3417,17064,3750],{"class":3430},[3417,17066,3876],{"class":3722},[3417,17068,10180],{"class":3430},[3417,17070,17071],{"class":3434},"\"Загальне\"",[3417,17073,13054],{"class":3430},[3417,17075,17076],{"class":3419,"line":4551},[3417,17077,13140],{"class":3430},[3417,17079,17080,17082,17084],{"class":3419,"line":4557},[3417,17081,3961],{"class":3960},[3417,17083,6021],{"class":3718},[3417,17085,17086],{"class":3430}," name.strip():\n",[3417,17088,17089,17091,17093,17095,17098],{"class":3419,"line":4565},[3417,17090,3974],{"class":3960},[3417,17092,3977],{"class":3722},[3417,17094,3738],{"class":3430},[3417,17096,17097],{"class":3434},"\"Назва продукту не може бути порожньою\"",[3417,17099,3445],{"class":3430},[3417,17101,17102,17104],{"class":3419,"line":4574},[3417,17103,3769],{"class":3718},[3417,17105,17106],{"class":3430},".name      = name.strip()\n",[3417,17108,17109,17111,17114],{"class":3419,"line":5657},[3417,17110,3769],{"class":3718},[3417,17112,17113],{"class":3430},".price     = price    ",[3417,17115,17116],{"class":3423},"# → PositiveFloat.__set__ (валідація)\n",[3417,17118,17119,17121,17124],{"class":3419,"line":5680},[3417,17120,3769],{"class":3718},[3417,17122,17123],{"class":3430},".stock     = stock    ",[3417,17125,17126],{"class":3423},"# → NonNegativeInt.__set__ (валідація)\n",[3417,17128,17129,17131,17134],{"class":3419,"line":5685},[3417,17130,3769],{"class":3718},[3417,17132,17133],{"class":3430},"._category = category ",[3417,17135,17136],{"class":3423},"# захищений: деталь реалізації\n",[3417,17138,17139],{"class":3419,"line":5691},[3417,17140,3452],{"emptyLinePlaceholder":3451},[3417,17142,17143],{"class":3419,"line":5700},[3417,17144,17145],{"class":3423},"    # ── Обчислювальні властивості (read-only) ─────────────────────────────────\n",[3417,17147,17148],{"class":3419,"line":5722},[3417,17149,3452],{"emptyLinePlaceholder":3451},[3417,17151,17152,17154],{"class":3419,"line":5727},[3417,17153,5452],{"class":3734},[3417,17155,5455],{"class":3722},[3417,17157,17158,17160,17163,17165,17167,17169,17171],{"class":3419,"line":5733},[3417,17159,3731],{"class":3718},[3417,17161,17162],{"class":3734}," is_available",[3417,17164,3738],{"class":3430},[3417,17166,3742],{"class":3741},[3417,17168,3950],{"class":3430},[3417,17170,11421],{"class":3722},[3417,17172,3726],{"class":3430},[3417,17174,17175],{"class":3419,"line":5740},[3417,17176,17177],{"class":3434},"        \"\"\"True, якщо товар є у наявності.\"\"\"\n",[3417,17179,17180,17182,17184,17187],{"class":3419,"line":5749},[3417,17181,4130],{"class":3960},[3417,17183,4133],{"class":3718},[3417,17185,17186],{"class":3430},".stock > ",[3417,17188,17189],{"class":3441},"0\n",[3417,17191,17192],{"class":3419,"line":5760},[3417,17193,3452],{"emptyLinePlaceholder":3451},[3417,17195,17196,17198],{"class":3419,"line":6271},[3417,17197,5452],{"class":3734},[3417,17199,5455],{"class":3722},[3417,17201,17202,17204,17207,17209,17211,17213,17215],{"class":3419,"line":6277},[3417,17203,3731],{"class":3718},[3417,17205,17206],{"class":3734}," category",[3417,17208,3738],{"class":3430},[3417,17210,3742],{"class":3741},[3417,17212,3950],{"class":3430},[3417,17214,3876],{"class":3722},[3417,17216,3726],{"class":3430},[3417,17218,17219],{"class":3419,"line":6289},[3417,17220,17221],{"class":3434},"        \"\"\"Категорія товару (лише читання ззовні).\"\"\"\n",[3417,17223,17224,17226,17228],{"class":3419,"line":6299},[3417,17225,4130],{"class":3960},[3417,17227,4133],{"class":3718},[3417,17229,17230],{"class":3430},"._category\n",[3417,17232,17233],{"class":3419,"line":6304},[3417,17234,3452],{"emptyLinePlaceholder":3451},[3417,17236,17237],{"class":3419,"line":6310},[3417,17238,17239],{"class":3423},"    # ── Методи бізнес-логіки ──────────────────────────────────────────────────\n",[3417,17241,17242],{"class":3419,"line":6315},[3417,17243,3452],{"emptyLinePlaceholder":3451},[3417,17245,17246,17248,17251,17253,17255,17257,17260,17262,17264,17266,17268],{"class":3419,"line":6322},[3417,17247,3731],{"class":3718},[3417,17249,17250],{"class":3734}," reserve",[3417,17252,3738],{"class":3430},[3417,17254,3742],{"class":3741},[3417,17256,3438],{"class":3430},[3417,17258,17259],{"class":3741},"qty",[3417,17261,3750],{"class":3430},[3417,17263,6030],{"class":3722},[3417,17265,3950],{"class":3430},[3417,17267,3953],{"class":3718},[3417,17269,3726],{"class":3430},[3417,17271,17272],{"class":3419,"line":6340},[3417,17273,17274],{"class":3434},"        \"\"\"Резервує qty одиниць товару (зменшує stock).\"\"\"\n",[3417,17276,17277,17279,17281,17283,17286,17288,17290,17292,17295,17297],{"class":3419,"line":6346},[3417,17278,3961],{"class":3960},[3417,17280,6021],{"class":3718},[3417,17282,6024],{"class":3734},[3417,17284,17285],{"class":3430},"(qty, ",[3417,17287,6030],{"class":3722},[3417,17289,10696],{"class":3430},[3417,17291,8056],{"class":3718},[3417,17293,17294],{"class":3430}," qty \u003C= ",[3417,17296,3967],{"class":3441},[3417,17298,3726],{"class":3430},[3417,17300,17301,17303,17305,17307,17309,17312,17314,17316,17318,17320],{"class":3419,"line":8484},[3417,17302,3974],{"class":3960},[3417,17304,3977],{"class":3722},[3417,17306,3738],{"class":3430},[3417,17308,4001],{"class":3718},[3417,17310,17311],{"class":3434},"\"qty має бути додатним int, отримано ",[3417,17313,4007],{"class":3718},[3417,17315,17259],{"class":3430},[3417,17317,9861],{"class":3718},[3417,17319,4015],{"class":3434},[3417,17321,3445],{"class":3430},[3417,17323,17324,17326,17329,17331],{"class":3419,"line":8509},[3417,17325,3961],{"class":3960},[3417,17327,17328],{"class":3430}," qty > ",[3417,17330,3742],{"class":3718},[3417,17332,17333],{"class":3430},".stock:\n",[3417,17335,17336,17338,17340],{"class":3419,"line":8520},[3417,17337,3974],{"class":3960},[3417,17339,3977],{"class":3722},[3417,17341,9825],{"class":3430},[3417,17343,17344,17346,17349,17351,17353,17355,17358,17360,17362,17364],{"class":3419,"line":8534},[3417,17345,9830],{"class":3718},[3417,17347,17348],{"class":3434},"\"Недостатньо '",[3417,17350,4744],{"class":3718},[3417,17352,10260],{"class":3430},[3417,17354,4012],{"class":3718},[3417,17356,17357],{"class":3434},"': потрібно ",[3417,17359,4007],{"class":3718},[3417,17361,17259],{"class":3430},[3417,17363,4012],{"class":3718},[3417,17365,10275],{"class":3434},[3417,17367,17368,17370,17373,17375,17378,17380],{"class":3419,"line":8542},[3417,17369,9830],{"class":3718},[3417,17371,17372],{"class":3434},"\"доступно ",[3417,17374,4744],{"class":3718},[3417,17376,17377],{"class":3430},".stock",[3417,17379,4012],{"class":3718},[3417,17381,9696],{"class":3434},[3417,17383,17384],{"class":3419,"line":8547},[3417,17385,9868],{"class":3430},[3417,17387,17388,17390],{"class":3419,"line":8552},[3417,17389,3769],{"class":3718},[3417,17391,17392],{"class":3430},".stock -= qty\n",[3417,17394,17395],{"class":3419,"line":8563},[3417,17396,3452],{"emptyLinePlaceholder":3451},[3417,17398,17399,17401,17404,17406,17408,17410,17412,17414,17416,17418,17420],{"class":3419,"line":8574},[3417,17400,3731],{"class":3718},[3417,17402,17403],{"class":3734}," release",[3417,17405,3738],{"class":3430},[3417,17407,3742],{"class":3741},[3417,17409,3438],{"class":3430},[3417,17411,17259],{"class":3741},[3417,17413,3750],{"class":3430},[3417,17415,6030],{"class":3722},[3417,17417,3950],{"class":3430},[3417,17419,3953],{"class":3718},[3417,17421,3726],{"class":3430},[3417,17423,17424],{"class":3419,"line":8583},[3417,17425,17426],{"class":3434},"        \"\"\"Повертає qty одиниць товару (збільшує stock).\"\"\"\n",[3417,17428,17429,17431,17433,17435,17437,17439,17441,17443,17445,17447],{"class":3419,"line":8593},[3417,17430,3961],{"class":3960},[3417,17432,6021],{"class":3718},[3417,17434,6024],{"class":3734},[3417,17436,17285],{"class":3430},[3417,17438,6030],{"class":3722},[3417,17440,10696],{"class":3430},[3417,17442,8056],{"class":3718},[3417,17444,17294],{"class":3430},[3417,17446,3967],{"class":3441},[3417,17448,3726],{"class":3430},[3417,17450,17451,17453,17455,17457,17459,17462],{"class":3419,"line":8598},[3417,17452,3974],{"class":3960},[3417,17454,3977],{"class":3722},[3417,17456,3738],{"class":3430},[3417,17458,4001],{"class":3718},[3417,17460,17461],{"class":3434},"\"qty має бути додатним int\"",[3417,17463,3445],{"class":3430},[3417,17465,17466,17468],{"class":3419,"line":8605},[3417,17467,3769],{"class":3718},[3417,17469,17470],{"class":3430},".stock += qty\n",[3417,17472,17473],{"class":3419,"line":8613},[3417,17474,3452],{"emptyLinePlaceholder":3451},[3417,17476,17477,17479,17481,17483,17485,17487,17489],{"class":3419,"line":8624},[3417,17478,3731],{"class":3718},[3417,17480,9979],{"class":3734},[3417,17482,3738],{"class":3430},[3417,17484,3742],{"class":3741},[3417,17486,3950],{"class":3430},[3417,17488,3876],{"class":3722},[3417,17490,3726],{"class":3430},[3417,17492,17493,17496,17499,17501,17503,17506,17508],{"class":3419,"line":10278},[3417,17494,17495],{"class":3430},"        status = ",[3417,17497,17498],{"class":3434},"\"✅\"",[3417,17500,8064],{"class":3960},[3417,17502,4133],{"class":3718},[3417,17504,17505],{"class":3430},".is_available ",[3417,17507,8070],{"class":3960},[3417,17509,17510],{"class":3434}," \"❌\"\n",[3417,17512,17513,17515],{"class":3419,"line":10316},[3417,17514,4130],{"class":3960},[3417,17516,9996],{"class":3430},[3417,17518,17519,17521,17523,17525,17527,17529,17531,17533,17535,17538],{"class":3419,"line":10321},[3417,17520,10001],{"class":3718},[3417,17522,15418],{"class":3434},[3417,17524,4744],{"class":3718},[3417,17526,10260],{"class":3430},[3417,17528,9861],{"class":3718},[3417,17530,15427],{"class":3434},[3417,17532,4744],{"class":3718},[3417,17534,15432],{"class":3430},[3417,17536,17537],{"class":3718},":.2f}",[3417,17539,17540],{"class":3434}," грн, \"\n",[3417,17542,17543,17545,17548,17550,17552,17554,17557,17560,17562],{"class":3419,"line":10326},[3417,17544,10001],{"class":3718},[3417,17546,17547],{"class":3434},"\"stock=",[3417,17549,4744],{"class":3718},[3417,17551,17377],{"class":3430},[3417,17553,4012],{"class":3718},[3417,17555,17556],{"class":3718}," {",[3417,17558,17559],{"class":3430},"status",[3417,17561,4012],{"class":3718},[3417,17563,10313],{"class":3434},[3417,17565,17566],{"class":3419,"line":10331},[3417,17567,10042],{"class":3430},[3408,17569,17572],{"className":3410,"code":17570,"filename":17571,"language":3412,"meta":3413,"style":3413},"from __future__ import annotations\nfrom dataclasses import dataclass, field\nfrom product import Product\n\nTAX_RATE = 0.20  # ПДВ 20%\n\n# Словник активних купонів {код: відсоток_знижки}\nVALID_COUPONS: dict[str, float] = {\n    \"SALE10\": 10.0,\n    \"VIP20\":  20.0,\n    \"WELCOME5\": 5.0,\n}\n\n\n@dataclass\nclass Receipt:\n    \"\"\"Чек замовлення — незмінний запис після checkout.\"\"\"\n    items:        list[tuple[str, int, float]]  # (назва, qty, ціна)\n    subtotal:     float\n    tax:          float\n    discount:     float\n    total:        float\n    coupon_code:  str | None = None\n\n    def to_text(self) -> str:\n        lines = [\"=\" * 42, \"        ЧАРТЕР ЗАМОВЛЕННЯ\", \"=\" * 42]\n        for name, qty, price in self.items:\n            lines.append(f\"  {name:\u003C20} {qty} × {price:>7.2f} грн\")\n        lines += [\n            \"-\" * 42,\n            f\"  Сума без ПДВ:    {self.subtotal - self.tax:>10.2f} грн\",\n            f\"  ПДВ (20%):       {self.tax:>10.2f} грн\",\n            f\"  Знижка{(' (' + self.coupon_code + ')') if self.coupon_code else '':>9}: {self.discount:>10.2f} грн\",\n            \"=\" * 42,\n            f\"  РАЗОМ:           {self.total:>10.2f} грн\",\n            \"=\" * 42,\n        ]\n        return \"\\n\".join(lines)\n\n\nclass Cart:\n    \"\"\"\n    Кошик покупця.\n\n    Публічний інтерфейс:\n        add(product, qty)    — додає товар\n        remove(product_name) — видаляє рядок\n        coupon               — геттер\u002Fсеттер\u002Fделітер купона\n        subtotal, tax, discount_amount, final_price — обчислювальні\n        checkout()           — оформляє замовлення, повертає Receipt\n\n    Захищені деталі:\n        _items               — внутрішній словник рядків кошика\n\n    Приватні (Name Mangling):\n        __discount_code      → _Cart__discount_code\n        __discount_percent   → _Cart__discount_percent\n        Захищені від перезапису підкласами!\n    \"\"\"\n\n    def __init__(self, owner_name: str):\n        self.owner_name        = owner_name\n        self._items: dict[str, dict] = {}     # {product.name: {product, qty}}\n        self.__discount_code:    str | None = None  # → _Cart__discount_code\n        self.__discount_percent: float      = 0.0   # → _Cart__discount_percent\n\n    # ── Купон — геттер + сеттер + делітер ────────────────────────────────────\n\n    @property\n    def coupon(self) -> str | None:\n        \"\"\"Поточний активний купон (або None).\"\"\"\n        return self.__discount_code\n\n    @coupon.setter\n    def coupon(self, code: str) -> None:\n        \"\"\"Активує купон після перевірки його валідності.\"\"\"\n        if not isinstance(code, str):\n            raise TypeError(\"Код купона має бути рядком\")\n        code = code.strip().upper()\n        if code not in VALID_COUPONS:\n            raise ValueError(\n                f\"Купон {code!r} не знайдено або вже не діє. \"\n                f\"Доступні: {list(VALID_COUPONS.keys())}\"\n            )\n        self.__discount_code    = code\n        self.__discount_percent = VALID_COUPONS[code]\n        print(f\"✅ Купон {code!r} активовано: знижка {self.__discount_percent:.0f}%\")\n\n    @coupon.deleter\n    def coupon(self) -> None:\n        \"\"\"Скасовує активний купон.\"\"\"\n        if self.__discount_code is None:\n            print(\"ℹ️  Активний купон відсутній\")\n            return\n        removed = self.__discount_code\n        self.__discount_code    = None\n        self.__discount_percent = 0.0\n        print(f\"🗑️  Купон {removed!r} скасовано\")\n\n    # ── Обчислювальні властивості (read-only) ─────────────────────────────────\n\n    @property\n    def subtotal(self) -> float:\n        \"\"\"Сума всіх товарів з ПДВ (до знижки).\"\"\"\n        return sum(\n            row[\"product\"].price * row[\"qty\"]\n            for row in self._items.values()\n        )\n\n    @property\n    def tax(self) -> float:\n        \"\"\"Сума ПДВ (20% від subtotal).\"\"\"\n        return round(self.subtotal * TAX_RATE \u002F (1 + TAX_RATE), 2)\n\n    @property\n    def discount_amount(self) -> float:\n        \"\"\"Сума знижки в гривнях.\"\"\"\n        return round(self.subtotal * self.__discount_percent \u002F 100, 2)\n\n    @property\n    def final_price(self) -> float:\n        \"\"\"Підсумкова сума до оплати.\"\"\"\n        return round(self.subtotal - self.discount_amount, 2)\n\n    @property\n    def item_count(self) -> int:\n        \"\"\"Загальна кількість одиниць товарів.\"\"\"\n        return sum(row[\"qty\"] for row in self._items.values())\n\n    # ── Методи бізнес-логіки ──────────────────────────────────────────────────\n\n    def add(self, product: Product, qty: int = 1) -> None:\n        \"\"\"Додає qty одиниць product у кошик і резервує stock.\"\"\"\n        if not isinstance(qty, int) or qty \u003C= 0:\n            raise ValueError(f\"qty має бути додатним int\")\n        if not product.is_available:\n            raise ValueError(f\"Товар '{product.name}' недоступний\")\n\n        product.reserve(qty)   # зменшує stock та валідує наявність\n\n        if product.name in self._items:\n            self._items[product.name][\"qty\"] += qty\n        else:\n            self._items[product.name] = {\"product\": product, \"qty\": qty}\n\n        print(f\"  + {product.name} × {qty}  ({product.price:.2f} грн\u002Fшт)\")\n\n    def remove(self, product_name: str) -> None:\n        \"\"\"Видаляє товар з кошика та повертає stock.\"\"\"\n        if product_name not in self._items:\n            raise KeyError(f\"'{product_name}' немає у кошику\")\n        row = self._items.pop(product_name)\n        row[\"product\"].release(row[\"qty\"])\n        print(f\"  - {product_name} видалено з кошика\")\n\n    def checkout(self) -> Receipt:\n        \"\"\"Оформлює замовлення: повертає Receipt та очищає кошик.\"\"\"\n        if not self._items:\n            raise ValueError(\"Кошик порожній — нічого оформляти\")\n\n        receipt = Receipt(\n            items=[\n                (row[\"product\"].name, row[\"qty\"], row[\"product\"].price)\n                for row in self._items.values()\n            ],\n            subtotal=self.subtotal,\n            tax=self.tax,\n            discount=self.discount_amount,\n            total=self.final_price,\n            coupon_code=self.__discount_code,\n        )\n        self._items.clear()\n        del self.coupon   # скидаємо купон після checkout\n        return receipt\n\n    def __repr__(self) -> str:\n        return (\n            f\"Cart({self.owner_name!r}, \"\n            f\"items={self.item_count}, \"\n            f\"total={self.final_price:.2f} грн)\"\n        )\n\n\nclass PremiumCart(Cart):\n    \"\"\"\n    Кошик преміум-клієнта: додаткова знижка за лояльністю.\n\n    Демонстрація Name Mangling:\n        self.__discount_code → _PremiumCart__discount_code (РІЗНЕ поле!)\n        super().__discount_code залишається _Cart__discount_code (НЕ перезаписано)\n    \"\"\"\n\n    LOYALTY_DISCOUNT = 5.0  # 5% додатково для VIP-клієнтів\n\n    def __init__(self, owner_name: str, loyalty_years: int = 1):\n        super().__init__(owner_name)\n        self.__discount_code = \"LOYALTY\"  # → _PremiumCart__discount_code\n        self._loyalty_years  = loyalty_years\n\n    @property\n    def loyalty_discount(self) -> float:\n        \"\"\"Знижка за лояльністю: 5% × кількість років.\"\"\"\n        return min(self.LOYALTY_DISCOUNT * self._loyalty_years, 25.0)\n\n    @property\n    def final_price(self) -> float:\n        \"\"\"\n        Перевизначена властивість: знижка купона + знижка за лояльністю.\n        super().final_price використовує _Cart__discount_code (купон),\n        а self.loyalty_discount — з _PremiumCart__discount_code.\n        \"\"\"\n        base = super().final_price\n        loyalty_saving = round(base * self.loyalty_discount \u002F 100, 2)\n        return round(base - loyalty_saving, 2)\n\n    def __repr__(self) -> str:\n        return (\n            f\"PremiumCart({self.owner_name!r}, \"\n            f\"loyalty={self.loyalty_discount:.0f}%, \"\n            f\"total={self.final_price:.2f} грн)\"\n        )\n","cart.py",[3399,17573,17574,17587,17599,17611,17615,17626,17630,17635,17649,17660,17671,17682,17686,17690,17694,17699,17708,17713,17734,17742,17749,17756,17763,17778,17782,17799,17828,17842,17880,17885,17896,17920,17937,17990,18001,18019,18029,18034,18048,18052,18056,18065,18069,18074,18078,18082,18087,18092,18097,18102,18107,18111,18116,18121,18125,18130,18135,18140,18145,18149,18153,18174,18181,18201,18221,18238,18242,18247,18251,18257,18278,18283,18292,18296,18301,18325,18330,18345,18358,18363,18378,18386,18402,18420,18424,18431,18438,18471,18475,18480,18496,18501,18516,18528,18532,18541,18550,18560,18583,18587,18591,18595,18601,18618,18623,18632,18648,18663,18667,18671,18677,18694,18699,18723,18727,18733,18750,18755,18781,18785,18791,18808,18813,18834,18838,18844,18861,18866,18890,18894,18898,18902,18937,18942,18964,18978,18988,19014,19019,19028,19033,19048,19061,19069,19087,19092,19133,19138,19165,19171,19187,19212,19223,19239,19262,19267,19282,19288,19299,19313,19318,19324,19333,19354,19368,19374,19387,19400,19413,19426,19439,19444,19452,19465,19473,19478,19495,19502,19519,19536,19554,19559,19564,19569,19584,19589,19595,19600,19606,19612,19618,19623,19628,19639,19644,19678,19690,19704,19712,19717,19724,19742,19748,19772,19777,19784,19801,19807,19813,19819,19825,19830,19842,19867,19881,19886,19903,19910,19926,19944,19959],{"__ignoreMap":3413},[3417,17575,17576,17578,17581,17584],{"class":3419,"line":3420},[3417,17577,12959],{"class":3960},[3417,17579,17580],{"class":3741}," __future__",[3417,17582,17583],{"class":3960}," import",[3417,17585,17586],{"class":3430}," annotations\n",[3417,17588,17589,17591,17594,17596],{"class":3419,"line":3427},[3417,17590,12959],{"class":3960},[3417,17592,17593],{"class":3430}," dataclasses ",[3417,17595,5375],{"class":3960},[3417,17597,17598],{"class":3430}," dataclass, field\n",[3417,17600,17601,17603,17606,17608],{"class":3419,"line":3448},[3417,17602,12959],{"class":3960},[3417,17604,17605],{"class":3430}," product ",[3417,17607,5375],{"class":3960},[3417,17609,17610],{"class":3430}," Product\n",[3417,17612,17613],{"class":3419,"line":3455},[3417,17614,3452],{"emptyLinePlaceholder":3451},[3417,17616,17617,17620,17623],{"class":3419,"line":3461},[3417,17618,17619],{"class":3430},"TAX_RATE = ",[3417,17621,17622],{"class":3441},"0.20",[3417,17624,17625],{"class":3423},"  # ПДВ 20%\n",[3417,17627,17628],{"class":3419,"line":3473},[3417,17629,3452],{"emptyLinePlaceholder":3451},[3417,17631,17632],{"class":3419,"line":3485},[3417,17633,17634],{"class":3423},"# Словник активних купонів {код: відсоток_знижки}\n",[3417,17636,17637,17640,17642,17644,17646],{"class":3419,"line":3490},[3417,17638,17639],{"class":3430},"VALID_COUPONS: dict[",[3417,17641,3876],{"class":3722},[3417,17643,3438],{"class":3430},[3417,17645,3536],{"class":3722},[3417,17647,17648],{"class":3430},"] = {\n",[3417,17650,17651,17654,17656,17658],{"class":3419,"line":3496},[3417,17652,17653],{"class":3434},"    \"SALE10\"",[3417,17655,3750],{"class":3430},[3417,17657,3812],{"class":3441},[3417,17659,13054],{"class":3430},[3417,17661,17662,17665,17667,17669],{"class":3419,"line":3645},[3417,17663,17664],{"class":3434},"    \"VIP20\"",[3417,17666,13084],{"class":3430},[3417,17668,11799],{"class":3441},[3417,17670,13054],{"class":3430},[3417,17672,17673,17676,17678,17680],{"class":3419,"line":3651},[3417,17674,17675],{"class":3434},"    \"WELCOME5\"",[3417,17677,3750],{"class":3430},[3417,17679,5598],{"class":3441},[3417,17681,13054],{"class":3430},[3417,17683,17684],{"class":3419,"line":3657},[3417,17685,3654],{"class":3430},[3417,17687,17688],{"class":3419,"line":3662},[3417,17689,3452],{"emptyLinePlaceholder":3451},[3417,17691,17692],{"class":3419,"line":3668},[3417,17693,3452],{"emptyLinePlaceholder":3451},[3417,17695,17696],{"class":3419,"line":3673},[3417,17697,17698],{"class":3734},"@dataclass\n",[3417,17700,17701,17703,17706],{"class":3419,"line":3679},[3417,17702,3719],{"class":3718},[3417,17704,17705],{"class":3722}," Receipt",[3417,17707,3726],{"class":3430},[3417,17709,17710],{"class":3419,"line":3685},[3417,17711,17712],{"class":3434},"    \"\"\"Чек замовлення — незмінний запис після checkout.\"\"\"\n",[3417,17714,17715,17718,17720,17722,17724,17726,17728,17731],{"class":3419,"line":4074},[3417,17716,17717],{"class":3430},"    items:        list[tuple[",[3417,17719,3876],{"class":3722},[3417,17721,3438],{"class":3430},[3417,17723,6030],{"class":3722},[3417,17725,3438],{"class":3430},[3417,17727,3536],{"class":3722},[3417,17729,17730],{"class":3430},"]]  ",[3417,17732,17733],{"class":3423},"# (назва, qty, ціна)\n",[3417,17735,17736,17739],{"class":3419,"line":4082},[3417,17737,17738],{"class":3430},"    subtotal:     ",[3417,17740,17741],{"class":3722},"float\n",[3417,17743,17744,17747],{"class":3419,"line":4104},[3417,17745,17746],{"class":3430},"    tax:          ",[3417,17748,17741],{"class":3722},[3417,17750,17751,17754],{"class":3419,"line":4109},[3417,17752,17753],{"class":3430},"    discount:     ",[3417,17755,17741],{"class":3722},[3417,17757,17758,17761],{"class":3419,"line":4127},[3417,17759,17760],{"class":3430},"    total:        ",[3417,17762,17741],{"class":3722},[3417,17764,17765,17768,17770,17772,17774,17776],{"class":3419,"line":4522},[3417,17766,17767],{"class":3430},"    coupon_code:  ",[3417,17769,3876],{"class":3722},[3417,17771,12706],{"class":3430},[3417,17773,3953],{"class":3718},[3417,17775,10180],{"class":3430},[3417,17777,9621],{"class":3718},[3417,17779,17780],{"class":3419,"line":4527},[3417,17781,3452],{"emptyLinePlaceholder":3451},[3417,17783,17784,17786,17789,17791,17793,17795,17797],{"class":3419,"line":4533},[3417,17785,3731],{"class":3718},[3417,17787,17788],{"class":3734}," to_text",[3417,17790,3738],{"class":3430},[3417,17792,3742],{"class":3741},[3417,17794,3950],{"class":3430},[3417,17796,3876],{"class":3722},[3417,17798,3726],{"class":3430},[3417,17800,17801,17804,17807,17810,17813,17815,17818,17820,17822,17824,17826],{"class":3419,"line":4546},[3417,17802,17803],{"class":3430},"        lines = [",[3417,17805,17806],{"class":3434},"\"=\"",[3417,17808,17809],{"class":3430}," * ",[3417,17811,17812],{"class":3441},"42",[3417,17814,3438],{"class":3430},[3417,17816,17817],{"class":3434},"\"        ЧАРТЕР ЗАМОВЛЕННЯ\"",[3417,17819,3438],{"class":3430},[3417,17821,17806],{"class":3434},[3417,17823,17809],{"class":3430},[3417,17825,17812],{"class":3441},[3417,17827,10645],{"class":3430},[3417,17829,17830,17832,17835,17837,17839],{"class":3419,"line":4551},[3417,17831,14918],{"class":3960},[3417,17833,17834],{"class":3430}," name, qty, price ",[3417,17836,10443],{"class":3960},[3417,17838,4133],{"class":3718},[3417,17840,17841],{"class":3430},".items:\n",[3417,17843,17844,17847,17849,17852,17854,17856,17859,17861,17863,17865,17868,17870,17872,17875,17878],{"class":3419,"line":4557},[3417,17845,17846],{"class":3430},"            lines.append(",[3417,17848,4001],{"class":3718},[3417,17850,17851],{"class":3434},"\"  ",[3417,17853,4007],{"class":3718},[3417,17855,9422],{"class":3430},[3417,17857,17858],{"class":3718},":\u003C20}",[3417,17860,17556],{"class":3718},[3417,17862,17259],{"class":3430},[3417,17864,4012],{"class":3718},[3417,17866,17867],{"class":3434}," × ",[3417,17869,4007],{"class":3718},[3417,17871,15326],{"class":3430},[3417,17873,17874],{"class":3718},":>7.2f}",[3417,17876,17877],{"class":3434}," грн\"",[3417,17879,3445],{"class":3430},[3417,17881,17882],{"class":3419,"line":4565},[3417,17883,17884],{"class":3430},"        lines += [\n",[3417,17886,17887,17890,17892,17894],{"class":3419,"line":4574},[3417,17888,17889],{"class":3434},"            \"-\"",[3417,17891,17809],{"class":3430},[3417,17893,17812],{"class":3441},[3417,17895,13054],{"class":3430},[3417,17897,17898,17900,17903,17905,17908,17910,17913,17916,17918],{"class":3419,"line":5657},[3417,17899,10001],{"class":3718},[3417,17901,17902],{"class":3434},"\"  Сума без ПДВ:    ",[3417,17904,4744],{"class":3718},[3417,17906,17907],{"class":3430},".subtotal - ",[3417,17909,3742],{"class":3718},[3417,17911,17912],{"class":3430},".tax",[3417,17914,17915],{"class":3718},":>10.2f}",[3417,17917,17877],{"class":3434},[3417,17919,13054],{"class":3430},[3417,17921,17922,17924,17927,17929,17931,17933,17935],{"class":3419,"line":5680},[3417,17923,10001],{"class":3718},[3417,17925,17926],{"class":3434},"\"  ПДВ (20%):       ",[3417,17928,4744],{"class":3718},[3417,17930,17912],{"class":3430},[3417,17932,17915],{"class":3718},[3417,17934,17877],{"class":3434},[3417,17936,13054],{"class":3430},[3417,17938,17939,17941,17944,17946,17948,17951,17953,17955,17958,17961,17963,17965,17967,17970,17972,17974,17977,17979,17981,17984,17986,17988],{"class":3419,"line":5685},[3417,17940,10001],{"class":3718},[3417,17942,17943],{"class":3434},"\"  Знижка",[3417,17945,4007],{"class":3718},[3417,17947,3738],{"class":3430},[3417,17949,17950],{"class":3434},"' ('",[3417,17952,14988],{"class":3430},[3417,17954,3742],{"class":3718},[3417,17956,17957],{"class":3430},".coupon_code + ",[3417,17959,17960],{"class":3434},"')'",[3417,17962,10696],{"class":3430},[3417,17964,15083],{"class":3960},[3417,17966,4133],{"class":3718},[3417,17968,17969],{"class":3430},".coupon_code ",[3417,17971,8070],{"class":3960},[3417,17973,15100],{"class":3434},[3417,17975,17976],{"class":3718},":>9}",[3417,17978,3750],{"class":3434},[3417,17980,4744],{"class":3718},[3417,17982,17983],{"class":3430},".discount",[3417,17985,17915],{"class":3718},[3417,17987,17877],{"class":3434},[3417,17989,13054],{"class":3430},[3417,17991,17992,17995,17997,17999],{"class":3419,"line":5691},[3417,17993,17994],{"class":3434},"            \"=\"",[3417,17996,17809],{"class":3430},[3417,17998,17812],{"class":3441},[3417,18000,13054],{"class":3430},[3417,18002,18003,18005,18008,18010,18013,18015,18017],{"class":3419,"line":5700},[3417,18004,10001],{"class":3718},[3417,18006,18007],{"class":3434},"\"  РАЗОМ:           ",[3417,18009,4744],{"class":3718},[3417,18011,18012],{"class":3430},".total",[3417,18014,17915],{"class":3718},[3417,18016,17877],{"class":3434},[3417,18018,13054],{"class":3430},[3417,18020,18021,18023,18025,18027],{"class":3419,"line":5722},[3417,18022,17994],{"class":3434},[3417,18024,17809],{"class":3430},[3417,18026,17812],{"class":3441},[3417,18028,13054],{"class":3430},[3417,18030,18031],{"class":3419,"line":5727},[3417,18032,18033],{"class":3430},"        ]\n",[3417,18035,18036,18038,18041,18043,18045],{"class":3419,"line":5733},[3417,18037,4130],{"class":3960},[3417,18039,18040],{"class":3434}," \"",[3417,18042,6476],{"class":6475},[3417,18044,4015],{"class":3434},[3417,18046,18047],{"class":3430},".join(lines)\n",[3417,18049,18050],{"class":3419,"line":5740},[3417,18051,3452],{"emptyLinePlaceholder":3451},[3417,18053,18054],{"class":3419,"line":5749},[3417,18055,3452],{"emptyLinePlaceholder":3451},[3417,18057,18058,18060,18063],{"class":3419,"line":5760},[3417,18059,3719],{"class":3718},[3417,18061,18062],{"class":3722}," Cart",[3417,18064,3726],{"class":3430},[3417,18066,18067],{"class":3419,"line":6271},[3417,18068,5877],{"class":3434},[3417,18070,18071],{"class":3419,"line":6277},[3417,18072,18073],{"class":3434},"    Кошик покупця.\n",[3417,18075,18076],{"class":3419,"line":6289},[3417,18077,3452],{"emptyLinePlaceholder":3451},[3417,18079,18080],{"class":3419,"line":6299},[3417,18081,16948],{"class":3434},[3417,18083,18084],{"class":3419,"line":6304},[3417,18085,18086],{"class":3434},"        add(product, qty)    — додає товар\n",[3417,18088,18089],{"class":3419,"line":6310},[3417,18090,18091],{"class":3434},"        remove(product_name) — видаляє рядок\n",[3417,18093,18094],{"class":3419,"line":6315},[3417,18095,18096],{"class":3434},"        coupon               — геттер\u002Fсеттер\u002Fделітер купона\n",[3417,18098,18099],{"class":3419,"line":6322},[3417,18100,18101],{"class":3434},"        subtotal, tax, discount_amount, final_price — обчислювальні\n",[3417,18103,18104],{"class":3419,"line":6340},[3417,18105,18106],{"class":3434},"        checkout()           — оформляє замовлення, повертає Receipt\n",[3417,18108,18109],{"class":3419,"line":6346},[3417,18110,3452],{"emptyLinePlaceholder":3451},[3417,18112,18113],{"class":3419,"line":8484},[3417,18114,18115],{"class":3434},"    Захищені деталі:\n",[3417,18117,18118],{"class":3419,"line":8509},[3417,18119,18120],{"class":3434},"        _items               — внутрішній словник рядків кошика\n",[3417,18122,18123],{"class":3419,"line":8520},[3417,18124,3452],{"emptyLinePlaceholder":3451},[3417,18126,18127],{"class":3419,"line":8534},[3417,18128,18129],{"class":3434},"    Приватні (Name Mangling):\n",[3417,18131,18132],{"class":3419,"line":8542},[3417,18133,18134],{"class":3434},"        __discount_code      → _Cart__discount_code\n",[3417,18136,18137],{"class":3419,"line":8547},[3417,18138,18139],{"class":3434},"        __discount_percent   → _Cart__discount_percent\n",[3417,18141,18142],{"class":3419,"line":8552},[3417,18143,18144],{"class":3434},"        Захищені від перезапису підкласами!\n",[3417,18146,18147],{"class":3419,"line":8563},[3417,18148,5877],{"class":3434},[3417,18150,18151],{"class":3419,"line":8574},[3417,18152,3452],{"emptyLinePlaceholder":3451},[3417,18154,18155,18157,18159,18161,18163,18165,18168,18170,18172],{"class":3419,"line":8583},[3417,18156,3731],{"class":3718},[3417,18158,3735],{"class":3734},[3417,18160,3738],{"class":3430},[3417,18162,3742],{"class":3741},[3417,18164,3438],{"class":3430},[3417,18166,18167],{"class":3741},"owner_name",[3417,18169,3750],{"class":3430},[3417,18171,3876],{"class":3722},[3417,18173,3764],{"class":3430},[3417,18175,18176,18178],{"class":3419,"line":8593},[3417,18177,3769],{"class":3718},[3417,18179,18180],{"class":3430},".owner_name        = owner_name\n",[3417,18182,18183,18185,18188,18190,18192,18195,18198],{"class":3419,"line":8598},[3417,18184,3769],{"class":3718},[3417,18186,18187],{"class":3430},"._items: dict[",[3417,18189,3876],{"class":3722},[3417,18191,3438],{"class":3430},[3417,18193,18194],{"class":3722},"dict",[3417,18196,18197],{"class":3430},"] = {}     ",[3417,18199,18200],{"class":3423},"# {product.name: {product, qty}}\n",[3417,18202,18203,18205,18208,18210,18212,18214,18216,18218],{"class":3419,"line":8605},[3417,18204,3769],{"class":3718},[3417,18206,18207],{"class":3430},".__discount_code:    ",[3417,18209,3876],{"class":3722},[3417,18211,12706],{"class":3430},[3417,18213,3953],{"class":3718},[3417,18215,10180],{"class":3430},[3417,18217,3953],{"class":3718},[3417,18219,18220],{"class":3423},"  # → _Cart__discount_code\n",[3417,18222,18223,18225,18228,18230,18233,18235],{"class":3419,"line":8613},[3417,18224,3769],{"class":3718},[3417,18226,18227],{"class":3430},".__discount_percent: ",[3417,18229,3536],{"class":3722},[3417,18231,18232],{"class":3430},"      = ",[3417,18234,6727],{"class":3441},[3417,18236,18237],{"class":3423},"   # → _Cart__discount_percent\n",[3417,18239,18240],{"class":3419,"line":8624},[3417,18241,3452],{"emptyLinePlaceholder":3451},[3417,18243,18244],{"class":3419,"line":10278},[3417,18245,18246],{"class":3423},"    # ── Купон — геттер + сеттер + делітер ────────────────────────────────────\n",[3417,18248,18249],{"class":3419,"line":10316},[3417,18250,3452],{"emptyLinePlaceholder":3451},[3417,18252,18253,18255],{"class":3419,"line":10321},[3417,18254,5452],{"class":3734},[3417,18256,5455],{"class":3722},[3417,18258,18259,18261,18264,18266,18268,18270,18272,18274,18276],{"class":3419,"line":10326},[3417,18260,3731],{"class":3718},[3417,18262,18263],{"class":3734}," coupon",[3417,18265,3738],{"class":3430},[3417,18267,3742],{"class":3741},[3417,18269,3950],{"class":3430},[3417,18271,3876],{"class":3722},[3417,18273,12706],{"class":3430},[3417,18275,3953],{"class":3718},[3417,18277,3726],{"class":3430},[3417,18279,18280],{"class":3419,"line":10331},[3417,18281,18282],{"class":3434},"        \"\"\"Поточний активний купон (або None).\"\"\"\n",[3417,18284,18285,18287,18289],{"class":3419,"line":10337},[3417,18286,4130],{"class":3960},[3417,18288,4133],{"class":3718},[3417,18290,18291],{"class":3430},".__discount_code\n",[3417,18293,18294],{"class":3419,"line":10360},[3417,18295,3452],{"emptyLinePlaceholder":3451},[3417,18297,18298],{"class":3419,"line":10368},[3417,18299,18300],{"class":3734},"    @coupon.setter\n",[3417,18302,18303,18305,18307,18309,18311,18313,18315,18317,18319,18321,18323],{"class":3419,"line":10373},[3417,18304,3731],{"class":3718},[3417,18306,18263],{"class":3734},[3417,18308,3738],{"class":3430},[3417,18310,3742],{"class":3741},[3417,18312,3438],{"class":3430},[3417,18314,3399],{"class":3741},[3417,18316,3750],{"class":3430},[3417,18318,3876],{"class":3722},[3417,18320,3950],{"class":3430},[3417,18322,3953],{"class":3718},[3417,18324,3726],{"class":3430},[3417,18326,18327],{"class":3419,"line":10382},[3417,18328,18329],{"class":3434},"        \"\"\"Активує купон після перевірки його валідності.\"\"\"\n",[3417,18331,18332,18334,18336,18338,18341,18343],{"class":3419,"line":10405},[3417,18333,3961],{"class":3960},[3417,18335,6021],{"class":3718},[3417,18337,6024],{"class":3734},[3417,18339,18340],{"class":3430},"(code, ",[3417,18342,3876],{"class":3722},[3417,18344,3764],{"class":3430},[3417,18346,18347,18349,18351,18353,18356],{"class":3419,"line":10410},[3417,18348,3974],{"class":3960},[3417,18350,6044],{"class":3722},[3417,18352,3738],{"class":3430},[3417,18354,18355],{"class":3434},"\"Код купона має бути рядком\"",[3417,18357,3445],{"class":3430},[3417,18359,18360],{"class":3419,"line":10416},[3417,18361,18362],{"class":3430},"        code = code.strip().upper()\n",[3417,18364,18365,18367,18370,18373,18375],{"class":3419,"line":10470},[3417,18366,3961],{"class":3960},[3417,18368,18369],{"class":3430}," code ",[3417,18371,18372],{"class":3718},"not",[3417,18374,12468],{"class":3718},[3417,18376,18377],{"class":3430}," VALID_COUPONS:\n",[3417,18379,18380,18382,18384],{"class":3419,"line":10495},[3417,18381,3974],{"class":3960},[3417,18383,3977],{"class":3722},[3417,18385,9825],{"class":3430},[3417,18387,18388,18390,18393,18395,18397,18399],{"class":3419,"line":10500},[3417,18389,9830],{"class":3718},[3417,18391,18392],{"class":3434},"\"Купон ",[3417,18394,4007],{"class":3718},[3417,18396,3399],{"class":3430},[3417,18398,9861],{"class":3718},[3417,18400,18401],{"class":3434}," не знайдено або вже не діє. \"\n",[3417,18403,18404,18406,18409,18411,18413,18416,18418],{"class":3419,"line":10506},[3417,18405,9830],{"class":3718},[3417,18407,18408],{"class":3434},"\"Доступні: ",[3417,18410,4007],{"class":3718},[3417,18412,3917],{"class":3722},[3417,18414,18415],{"class":3430},"(VALID_COUPONS.keys())",[3417,18417,4012],{"class":3718},[3417,18419,9696],{"class":3434},[3417,18421,18422],{"class":3419,"line":10533},[3417,18423,9868],{"class":3430},[3417,18425,18426,18428],{"class":3419,"line":10538},[3417,18427,3769],{"class":3718},[3417,18429,18430],{"class":3430},".__discount_code    = code\n",[3417,18432,18433,18435],{"class":3419,"line":10544},[3417,18434,3769],{"class":3718},[3417,18436,18437],{"class":3430},".__discount_percent = VALID_COUPONS[code]\n",[3417,18439,18440,18442,18444,18446,18449,18451,18453,18455,18458,18460,18463,18466,18469],{"class":3419,"line":10550},[3417,18441,4734],{"class":3734},[3417,18443,3738],{"class":3430},[3417,18445,4001],{"class":3718},[3417,18447,18448],{"class":3434},"\"✅ Купон ",[3417,18450,4007],{"class":3718},[3417,18452,3399],{"class":3430},[3417,18454,9861],{"class":3718},[3417,18456,18457],{"class":3434}," активовано: знижка ",[3417,18459,4744],{"class":3718},[3417,18461,18462],{"class":3430},".__discount_percent",[3417,18464,18465],{"class":3718},":.0f}",[3417,18467,18468],{"class":3434},"%\"",[3417,18470,3445],{"class":3430},[3417,18472,18473],{"class":3419,"line":10584},[3417,18474,3452],{"emptyLinePlaceholder":3451},[3417,18476,18477],{"class":3419,"line":10613},[3417,18478,18479],{"class":3734},"    @coupon.deleter\n",[3417,18481,18482,18484,18486,18488,18490,18492,18494],{"class":3419,"line":10642},[3417,18483,3731],{"class":3718},[3417,18485,18263],{"class":3734},[3417,18487,3738],{"class":3430},[3417,18489,3742],{"class":3741},[3417,18491,3950],{"class":3430},[3417,18493,3953],{"class":3718},[3417,18495,3726],{"class":3430},[3417,18497,18498],{"class":3419,"line":10648},[3417,18499,18500],{"class":3434},"        \"\"\"Скасовує активний купон.\"\"\"\n",[3417,18502,18503,18505,18507,18510,18512,18514],{"class":3419,"line":10653},[3417,18504,3961],{"class":3960},[3417,18506,4133],{"class":3718},[3417,18508,18509],{"class":3430},".__discount_code ",[3417,18511,8115],{"class":3718},[3417,18513,8073],{"class":3718},[3417,18515,3726],{"class":3430},[3417,18517,18518,18521,18523,18526],{"class":3419,"line":10666},[3417,18519,18520],{"class":3734},"            print",[3417,18522,3738],{"class":3430},[3417,18524,18525],{"class":3434},"\"ℹ️  Активний купон відсутній\"",[3417,18527,3445],{"class":3430},[3417,18529,18530],{"class":3419,"line":10674},[3417,18531,12747],{"class":3960},[3417,18533,18534,18537,18539],{"class":3419,"line":10680},[3417,18535,18536],{"class":3430},"        removed = ",[3417,18538,3742],{"class":3718},[3417,18540,18291],{"class":3430},[3417,18542,18543,18545,18548],{"class":3419,"line":10704},[3417,18544,3769],{"class":3718},[3417,18546,18547],{"class":3430},".__discount_code    = ",[3417,18549,9621],{"class":3718},[3417,18551,18552,18554,18557],{"class":3419,"line":15202},[3417,18553,3769],{"class":3718},[3417,18555,18556],{"class":3430},".__discount_percent = ",[3417,18558,18559],{"class":3441},"0.0\n",[3417,18561,18562,18564,18566,18568,18571,18573,18576,18578,18581],{"class":3419,"line":15208},[3417,18563,4734],{"class":3734},[3417,18565,3738],{"class":3430},[3417,18567,4001],{"class":3718},[3417,18569,18570],{"class":3434},"\"🗑️  Купон ",[3417,18572,4007],{"class":3718},[3417,18574,18575],{"class":3430},"removed",[3417,18577,9861],{"class":3718},[3417,18579,18580],{"class":3434}," скасовано\"",[3417,18582,3445],{"class":3430},[3417,18584,18585],{"class":3419,"line":15217},[3417,18586,3452],{"emptyLinePlaceholder":3451},[3417,18588,18589],{"class":3419,"line":15254},[3417,18590,17145],{"class":3423},[3417,18592,18593],{"class":3419,"line":15259},[3417,18594,3452],{"emptyLinePlaceholder":3451},[3417,18596,18597,18599],{"class":3419,"line":15265},[3417,18598,5452],{"class":3734},[3417,18600,5455],{"class":3722},[3417,18602,18603,18605,18608,18610,18612,18614,18616],{"class":3419,"line":15274},[3417,18604,3731],{"class":3718},[3417,18606,18607],{"class":3734}," subtotal",[3417,18609,3738],{"class":3430},[3417,18611,3742],{"class":3741},[3417,18613,3950],{"class":3430},[3417,18615,3536],{"class":3722},[3417,18617,3726],{"class":3430},[3417,18619,18620],{"class":3419,"line":15283},[3417,18621,18622],{"class":3434},"        \"\"\"Сума всіх товарів з ПДВ (до знижки).\"\"\"\n",[3417,18624,18625,18627,18630],{"class":3419,"line":15295},[3417,18626,4130],{"class":3960},[3417,18628,18629],{"class":3734}," sum",[3417,18631,9825],{"class":3430},[3417,18633,18634,18637,18640,18643,18646],{"class":3419,"line":15300},[3417,18635,18636],{"class":3430},"            row[",[3417,18638,18639],{"class":3434},"\"product\"",[3417,18641,18642],{"class":3430},"].price * row[",[3417,18644,18645],{"class":3434},"\"qty\"",[3417,18647,10645],{"class":3430},[3417,18649,18650,18653,18656,18658,18660],{"class":3419,"line":15305},[3417,18651,18652],{"class":3960},"            for",[3417,18654,18655],{"class":3430}," row ",[3417,18657,10443],{"class":3960},[3417,18659,4133],{"class":3718},[3417,18661,18662],{"class":3430},"._items.values()\n",[3417,18664,18665],{"class":3419,"line":15357},[3417,18666,10042],{"class":3430},[3417,18668,18669],{"class":3419,"line":15365},[3417,18670,3452],{"emptyLinePlaceholder":3451},[3417,18672,18673,18675],{"class":3419,"line":15373},[3417,18674,5452],{"class":3734},[3417,18676,5455],{"class":3722},[3417,18678,18679,18681,18684,18686,18688,18690,18692],{"class":3419,"line":15381},[3417,18680,3731],{"class":3718},[3417,18682,18683],{"class":3734}," tax",[3417,18685,3738],{"class":3430},[3417,18687,3742],{"class":3741},[3417,18689,3950],{"class":3430},[3417,18691,3536],{"class":3722},[3417,18693,3726],{"class":3430},[3417,18695,18696],{"class":3419,"line":15389},[3417,18697,18698],{"class":3434},"        \"\"\"Сума ПДВ (20% від subtotal).\"\"\"\n",[3417,18700,18701,18703,18706,18708,18710,18713,18715,18718,18721],{"class":3419,"line":15394},[3417,18702,4130],{"class":3960},[3417,18704,18705],{"class":3734}," round",[3417,18707,3738],{"class":3430},[3417,18709,3742],{"class":3718},[3417,18711,18712],{"class":3430},".subtotal * TAX_RATE \u002F (",[3417,18714,10099],{"class":3441},[3417,18716,18717],{"class":3430}," + TAX_RATE), ",[3417,18719,18720],{"class":3441},"2",[3417,18722,3445],{"class":3430},[3417,18724,18725],{"class":3419,"line":15411},[3417,18726,3452],{"emptyLinePlaceholder":3451},[3417,18728,18729,18731],{"class":3419,"line":15449},[3417,18730,5452],{"class":3734},[3417,18732,5455],{"class":3722},[3417,18734,18735,18737,18740,18742,18744,18746,18748],{"class":3419,"line":15454},[3417,18736,3731],{"class":3718},[3417,18738,18739],{"class":3734}," discount_amount",[3417,18741,3738],{"class":3430},[3417,18743,3742],{"class":3741},[3417,18745,3950],{"class":3430},[3417,18747,3536],{"class":3722},[3417,18749,3726],{"class":3430},[3417,18751,18752],{"class":3419,"line":15459},[3417,18753,18754],{"class":3434},"        \"\"\"Сума знижки в гривнях.\"\"\"\n",[3417,18756,18757,18759,18761,18763,18765,18768,18770,18773,18775,18777,18779],{"class":3419,"line":15464},[3417,18758,4130],{"class":3960},[3417,18760,18705],{"class":3734},[3417,18762,3738],{"class":3430},[3417,18764,3742],{"class":3718},[3417,18766,18767],{"class":3430},".subtotal * ",[3417,18769,3742],{"class":3718},[3417,18771,18772],{"class":3430},".__discount_percent \u002F ",[3417,18774,9041],{"class":3441},[3417,18776,3438],{"class":3430},[3417,18778,18720],{"class":3441},[3417,18780,3445],{"class":3430},[3417,18782,18783],{"class":3419,"line":15476},[3417,18784,3452],{"emptyLinePlaceholder":3451},[3417,18786,18787,18789],{"class":3419,"line":15505},[3417,18788,5452],{"class":3734},[3417,18790,5455],{"class":3722},[3417,18792,18793,18795,18798,18800,18802,18804,18806],{"class":3419,"line":15513},[3417,18794,3731],{"class":3718},[3417,18796,18797],{"class":3734}," final_price",[3417,18799,3738],{"class":3430},[3417,18801,3742],{"class":3741},[3417,18803,3950],{"class":3430},[3417,18805,3536],{"class":3722},[3417,18807,3726],{"class":3430},[3417,18809,18810],{"class":3419,"line":15536},[3417,18811,18812],{"class":3434},"        \"\"\"Підсумкова сума до оплати.\"\"\"\n",[3417,18814,18815,18817,18819,18821,18823,18825,18827,18830,18832],{"class":3419,"line":15541},[3417,18816,4130],{"class":3960},[3417,18818,18705],{"class":3734},[3417,18820,3738],{"class":3430},[3417,18822,3742],{"class":3718},[3417,18824,17907],{"class":3430},[3417,18826,3742],{"class":3718},[3417,18828,18829],{"class":3430},".discount_amount, ",[3417,18831,18720],{"class":3441},[3417,18833,3445],{"class":3430},[3417,18835,18836],{"class":3419,"line":15557},[3417,18837,3452],{"emptyLinePlaceholder":3451},[3417,18839,18840,18842],{"class":3419,"line":15563},[3417,18841,5452],{"class":3734},[3417,18843,5455],{"class":3722},[3417,18845,18846,18848,18851,18853,18855,18857,18859],{"class":3419,"line":15591},[3417,18847,3731],{"class":3718},[3417,18849,18850],{"class":3734}," item_count",[3417,18852,3738],{"class":3430},[3417,18854,3742],{"class":3741},[3417,18856,3950],{"class":3430},[3417,18858,6030],{"class":3722},[3417,18860,3726],{"class":3430},[3417,18862,18863],{"class":3419,"line":15618},[3417,18864,18865],{"class":3434},"        \"\"\"Загальна кількість одиниць товарів.\"\"\"\n",[3417,18867,18868,18870,18872,18875,18877,18879,18881,18883,18885,18887],{"class":3419,"line":15646},[3417,18869,4130],{"class":3960},[3417,18871,18629],{"class":3734},[3417,18873,18874],{"class":3430},"(row[",[3417,18876,18645],{"class":3434},[3417,18878,10723],{"class":3430},[3417,18880,10437],{"class":3960},[3417,18882,18655],{"class":3430},[3417,18884,10443],{"class":3960},[3417,18886,4133],{"class":3718},[3417,18888,18889],{"class":3430},"._items.values())\n",[3417,18891,18892],{"class":3419,"line":15673},[3417,18893,3452],{"emptyLinePlaceholder":3451},[3417,18895,18896],{"class":3419,"line":15701},[3417,18897,17239],{"class":3423},[3417,18899,18900],{"class":3419,"line":15706},[3417,18901,3452],{"emptyLinePlaceholder":3451},[3417,18903,18904,18906,18909,18911,18913,18915,18918,18921,18923,18925,18927,18929,18931,18933,18935],{"class":3419,"line":15718},[3417,18905,3731],{"class":3718},[3417,18907,18908],{"class":3734}," add",[3417,18910,3738],{"class":3430},[3417,18912,3742],{"class":3741},[3417,18914,3438],{"class":3430},[3417,18916,18917],{"class":3741},"product",[3417,18919,18920],{"class":3430},": Product, ",[3417,18922,17259],{"class":3741},[3417,18924,3750],{"class":3430},[3417,18926,6030],{"class":3722},[3417,18928,10180],{"class":3430},[3417,18930,10099],{"class":3441},[3417,18932,3950],{"class":3430},[3417,18934,3953],{"class":3718},[3417,18936,3726],{"class":3430},[3417,18938,18939],{"class":3419,"line":15725},[3417,18940,18941],{"class":3434},"        \"\"\"Додає qty одиниць product у кошик і резервує stock.\"\"\"\n",[3417,18943,18944,18946,18948,18950,18952,18954,18956,18958,18960,18962],{"class":3419,"line":15730},[3417,18945,3961],{"class":3960},[3417,18947,6021],{"class":3718},[3417,18949,6024],{"class":3734},[3417,18951,17285],{"class":3430},[3417,18953,6030],{"class":3722},[3417,18955,10696],{"class":3430},[3417,18957,8056],{"class":3718},[3417,18959,17294],{"class":3430},[3417,18961,3967],{"class":3441},[3417,18963,3726],{"class":3430},[3417,18965,18966,18968,18970,18972,18974,18976],{"class":3419,"line":15741},[3417,18967,3974],{"class":3960},[3417,18969,3977],{"class":3722},[3417,18971,3738],{"class":3430},[3417,18973,4001],{"class":3718},[3417,18975,17461],{"class":3434},[3417,18977,3445],{"class":3430},[3417,18979,18981,18983,18985],{"class":3419,"line":18980},136,[3417,18982,3961],{"class":3960},[3417,18984,6021],{"class":3718},[3417,18986,18987],{"class":3430}," product.is_available:\n",[3417,18989,18991,18993,18995,18997,18999,19002,19004,19007,19009,19012],{"class":3419,"line":18990},137,[3417,18992,3974],{"class":3960},[3417,18994,3977],{"class":3722},[3417,18996,3738],{"class":3430},[3417,18998,4001],{"class":3718},[3417,19000,19001],{"class":3434},"\"Товар '",[3417,19003,4007],{"class":3718},[3417,19005,19006],{"class":3430},"product.name",[3417,19008,4012],{"class":3718},[3417,19010,19011],{"class":3434},"' недоступний\"",[3417,19013,3445],{"class":3430},[3417,19015,19017],{"class":3419,"line":19016},138,[3417,19018,3452],{"emptyLinePlaceholder":3451},[3417,19020,19022,19025],{"class":3419,"line":19021},139,[3417,19023,19024],{"class":3430},"        product.reserve(qty)   ",[3417,19026,19027],{"class":3423},"# зменшує stock та валідує наявність\n",[3417,19029,19031],{"class":3419,"line":19030},140,[3417,19032,3452],{"emptyLinePlaceholder":3451},[3417,19034,19036,19038,19041,19043,19045],{"class":3419,"line":19035},141,[3417,19037,3961],{"class":3960},[3417,19039,19040],{"class":3430}," product.name ",[3417,19042,10443],{"class":3718},[3417,19044,4133],{"class":3718},[3417,19046,19047],{"class":3430},"._items:\n",[3417,19049,19051,19053,19056,19058],{"class":3419,"line":19050},142,[3417,19052,4403],{"class":3718},[3417,19054,19055],{"class":3430},"._items[product.name][",[3417,19057,18645],{"class":3434},[3417,19059,19060],{"class":3430},"] += qty\n",[3417,19062,19064,19067],{"class":3419,"line":19063},143,[3417,19065,19066],{"class":3960},"        else",[3417,19068,3726],{"class":3430},[3417,19070,19072,19074,19077,19079,19082,19084],{"class":3419,"line":19071},144,[3417,19073,4403],{"class":3718},[3417,19075,19076],{"class":3430},"._items[product.name] = {",[3417,19078,18639],{"class":3434},[3417,19080,19081],{"class":3430},": product, ",[3417,19083,18645],{"class":3434},[3417,19085,19086],{"class":3430},": qty}\n",[3417,19088,19090],{"class":3419,"line":19089},145,[3417,19091,3452],{"emptyLinePlaceholder":3451},[3417,19093,19095,19097,19099,19101,19104,19106,19108,19110,19112,19114,19116,19118,19121,19123,19126,19128,19131],{"class":3419,"line":19094},146,[3417,19096,4734],{"class":3734},[3417,19098,3738],{"class":3430},[3417,19100,4001],{"class":3718},[3417,19102,19103],{"class":3434},"\"  + ",[3417,19105,4007],{"class":3718},[3417,19107,19006],{"class":3430},[3417,19109,4012],{"class":3718},[3417,19111,17867],{"class":3434},[3417,19113,4007],{"class":3718},[3417,19115,17259],{"class":3430},[3417,19117,4012],{"class":3718},[3417,19119,19120],{"class":3434},"  (",[3417,19122,4007],{"class":3718},[3417,19124,19125],{"class":3430},"product.price",[3417,19127,17537],{"class":3718},[3417,19129,19130],{"class":3434}," грн\u002Fшт)\"",[3417,19132,3445],{"class":3430},[3417,19134,19136],{"class":3419,"line":19135},147,[3417,19137,3452],{"emptyLinePlaceholder":3451},[3417,19139,19141,19143,19146,19148,19150,19152,19155,19157,19159,19161,19163],{"class":3419,"line":19140},148,[3417,19142,3731],{"class":3718},[3417,19144,19145],{"class":3734}," remove",[3417,19147,3738],{"class":3430},[3417,19149,3742],{"class":3741},[3417,19151,3438],{"class":3430},[3417,19153,19154],{"class":3741},"product_name",[3417,19156,3750],{"class":3430},[3417,19158,3876],{"class":3722},[3417,19160,3950],{"class":3430},[3417,19162,3953],{"class":3718},[3417,19164,3726],{"class":3430},[3417,19166,19168],{"class":3419,"line":19167},149,[3417,19169,19170],{"class":3434},"        \"\"\"Видаляє товар з кошика та повертає stock.\"\"\"\n",[3417,19172,19174,19176,19179,19181,19183,19185],{"class":3419,"line":19173},150,[3417,19175,3961],{"class":3960},[3417,19177,19178],{"class":3430}," product_name ",[3417,19180,18372],{"class":3718},[3417,19182,12468],{"class":3718},[3417,19184,4133],{"class":3718},[3417,19186,19047],{"class":3430},[3417,19188,19190,19192,19195,19197,19199,19201,19203,19205,19207,19210],{"class":3419,"line":19189},151,[3417,19191,3974],{"class":3960},[3417,19193,19194],{"class":3722}," KeyError",[3417,19196,3738],{"class":3430},[3417,19198,4001],{"class":3718},[3417,19200,9833],{"class":3434},[3417,19202,4007],{"class":3718},[3417,19204,19154],{"class":3430},[3417,19206,4012],{"class":3718},[3417,19208,19209],{"class":3434},"' немає у кошику\"",[3417,19211,3445],{"class":3430},[3417,19213,19215,19218,19220],{"class":3419,"line":19214},152,[3417,19216,19217],{"class":3430},"        row = ",[3417,19219,3742],{"class":3718},[3417,19221,19222],{"class":3430},"._items.pop(product_name)\n",[3417,19224,19226,19229,19231,19234,19236],{"class":3419,"line":19225},153,[3417,19227,19228],{"class":3430},"        row[",[3417,19230,18639],{"class":3434},[3417,19232,19233],{"class":3430},"].release(row[",[3417,19235,18645],{"class":3434},[3417,19237,19238],{"class":3430},"])\n",[3417,19240,19242,19244,19246,19248,19251,19253,19255,19257,19260],{"class":3419,"line":19241},154,[3417,19243,4734],{"class":3734},[3417,19245,3738],{"class":3430},[3417,19247,4001],{"class":3718},[3417,19249,19250],{"class":3434},"\"  - ",[3417,19252,4007],{"class":3718},[3417,19254,19154],{"class":3430},[3417,19256,4012],{"class":3718},[3417,19258,19259],{"class":3434}," видалено з кошика\"",[3417,19261,3445],{"class":3430},[3417,19263,19265],{"class":3419,"line":19264},155,[3417,19266,3452],{"emptyLinePlaceholder":3451},[3417,19268,19270,19272,19275,19277,19279],{"class":3419,"line":19269},156,[3417,19271,3731],{"class":3718},[3417,19273,19274],{"class":3734}," checkout",[3417,19276,3738],{"class":3430},[3417,19278,3742],{"class":3741},[3417,19280,19281],{"class":3430},") -> Receipt:\n",[3417,19283,19285],{"class":3419,"line":19284},157,[3417,19286,19287],{"class":3434},"        \"\"\"Оформлює замовлення: повертає Receipt та очищає кошик.\"\"\"\n",[3417,19289,19291,19293,19295,19297],{"class":3419,"line":19290},158,[3417,19292,3961],{"class":3960},[3417,19294,6021],{"class":3718},[3417,19296,4133],{"class":3718},[3417,19298,19047],{"class":3430},[3417,19300,19302,19304,19306,19308,19311],{"class":3419,"line":19301},159,[3417,19303,3974],{"class":3960},[3417,19305,3977],{"class":3722},[3417,19307,3738],{"class":3430},[3417,19309,19310],{"class":3434},"\"Кошик порожній — нічого оформляти\"",[3417,19312,3445],{"class":3430},[3417,19314,19316],{"class":3419,"line":19315},160,[3417,19317,3452],{"emptyLinePlaceholder":3451},[3417,19319,19321],{"class":3419,"line":19320},161,[3417,19322,19323],{"class":3430},"        receipt = Receipt(\n",[3417,19325,19327,19330],{"class":3419,"line":19326},162,[3417,19328,19329],{"class":3741},"            items",[3417,19331,19332],{"class":3430},"=[\n",[3417,19334,19336,19339,19341,19344,19346,19349,19351],{"class":3419,"line":19335},163,[3417,19337,19338],{"class":3430},"                (row[",[3417,19340,18639],{"class":3434},[3417,19342,19343],{"class":3430},"].name, row[",[3417,19345,18645],{"class":3434},[3417,19347,19348],{"class":3430},"], row[",[3417,19350,18639],{"class":3434},[3417,19352,19353],{"class":3430},"].price)\n",[3417,19355,19357,19360,19362,19364,19366],{"class":3419,"line":19356},164,[3417,19358,19359],{"class":3960},"                for",[3417,19361,18655],{"class":3430},[3417,19363,10443],{"class":3960},[3417,19365,4133],{"class":3718},[3417,19367,18662],{"class":3430},[3417,19369,19371],{"class":3419,"line":19370},165,[3417,19372,19373],{"class":3430},"            ],\n",[3417,19375,19377,19380,19382,19384],{"class":3419,"line":19376},166,[3417,19378,19379],{"class":3741},"            subtotal",[3417,19381,7334],{"class":3430},[3417,19383,3742],{"class":3718},[3417,19385,19386],{"class":3430},".subtotal,\n",[3417,19388,19390,19393,19395,19397],{"class":3419,"line":19389},167,[3417,19391,19392],{"class":3741},"            tax",[3417,19394,7334],{"class":3430},[3417,19396,3742],{"class":3718},[3417,19398,19399],{"class":3430},".tax,\n",[3417,19401,19403,19406,19408,19410],{"class":3419,"line":19402},168,[3417,19404,19405],{"class":3741},"            discount",[3417,19407,7334],{"class":3430},[3417,19409,3742],{"class":3718},[3417,19411,19412],{"class":3430},".discount_amount,\n",[3417,19414,19416,19419,19421,19423],{"class":3419,"line":19415},169,[3417,19417,19418],{"class":3741},"            total",[3417,19420,7334],{"class":3430},[3417,19422,3742],{"class":3718},[3417,19424,19425],{"class":3430},".final_price,\n",[3417,19427,19429,19432,19434,19436],{"class":3419,"line":19428},170,[3417,19430,19431],{"class":3741},"            coupon_code",[3417,19433,7334],{"class":3430},[3417,19435,3742],{"class":3718},[3417,19437,19438],{"class":3430},".__discount_code,\n",[3417,19440,19442],{"class":3419,"line":19441},171,[3417,19443,10042],{"class":3430},[3417,19445,19447,19449],{"class":3419,"line":19446},172,[3417,19448,3769],{"class":3718},[3417,19450,19451],{"class":3430},"._items.clear()\n",[3417,19453,19455,19457,19459,19462],{"class":3419,"line":19454},173,[3417,19456,6292],{"class":3960},[3417,19458,4133],{"class":3718},[3417,19460,19461],{"class":3430},".coupon   ",[3417,19463,19464],{"class":3423},"# скидаємо купон після checkout\n",[3417,19466,19468,19470],{"class":3419,"line":19467},174,[3417,19469,4130],{"class":3960},[3417,19471,19472],{"class":3430}," receipt\n",[3417,19474,19476],{"class":3419,"line":19475},175,[3417,19477,3452],{"emptyLinePlaceholder":3451},[3417,19479,19481,19483,19485,19487,19489,19491,19493],{"class":3419,"line":19480},176,[3417,19482,3731],{"class":3718},[3417,19484,9979],{"class":3734},[3417,19486,3738],{"class":3430},[3417,19488,3742],{"class":3741},[3417,19490,3950],{"class":3430},[3417,19492,3876],{"class":3722},[3417,19494,3726],{"class":3430},[3417,19496,19498,19500],{"class":3419,"line":19497},177,[3417,19499,4130],{"class":3960},[3417,19501,9996],{"class":3430},[3417,19503,19505,19507,19510,19512,19515,19517],{"class":3419,"line":19504},178,[3417,19506,10001],{"class":3718},[3417,19508,19509],{"class":3434},"\"Cart(",[3417,19511,4744],{"class":3718},[3417,19513,19514],{"class":3430},".owner_name",[3417,19516,9861],{"class":3718},[3417,19518,10275],{"class":3434},[3417,19520,19522,19524,19527,19529,19532,19534],{"class":3419,"line":19521},179,[3417,19523,10001],{"class":3718},[3417,19525,19526],{"class":3434},"\"items=",[3417,19528,4744],{"class":3718},[3417,19530,19531],{"class":3430},".item_count",[3417,19533,4012],{"class":3718},[3417,19535,10275],{"class":3434},[3417,19537,19539,19541,19544,19546,19549,19551],{"class":3419,"line":19538},180,[3417,19540,10001],{"class":3718},[3417,19542,19543],{"class":3434},"\"total=",[3417,19545,4744],{"class":3718},[3417,19547,19548],{"class":3430},".final_price",[3417,19550,17537],{"class":3718},[3417,19552,19553],{"class":3434}," грн)\"\n",[3417,19555,19557],{"class":3419,"line":19556},181,[3417,19558,10042],{"class":3430},[3417,19560,19562],{"class":3419,"line":19561},182,[3417,19563,3452],{"emptyLinePlaceholder":3451},[3417,19565,19567],{"class":3419,"line":19566},183,[3417,19568,3452],{"emptyLinePlaceholder":3451},[3417,19570,19572,19574,19577,19579,19582],{"class":3419,"line":19571},184,[3417,19573,3719],{"class":3718},[3417,19575,19576],{"class":3722}," PremiumCart",[3417,19578,3738],{"class":3430},[3417,19580,19581],{"class":3722},"Cart",[3417,19583,3764],{"class":3430},[3417,19585,19587],{"class":3419,"line":19586},185,[3417,19588,5877],{"class":3434},[3417,19590,19592],{"class":3419,"line":19591},186,[3417,19593,19594],{"class":3434},"    Кошик преміум-клієнта: додаткова знижка за лояльністю.\n",[3417,19596,19598],{"class":3419,"line":19597},187,[3417,19599,3452],{"emptyLinePlaceholder":3451},[3417,19601,19603],{"class":3419,"line":19602},188,[3417,19604,19605],{"class":3434},"    Демонстрація Name Mangling:\n",[3417,19607,19609],{"class":3419,"line":19608},189,[3417,19610,19611],{"class":3434},"        self.__discount_code → _PremiumCart__discount_code (РІЗНЕ поле!)\n",[3417,19613,19615],{"class":3419,"line":19614},190,[3417,19616,19617],{"class":3434},"        super().__discount_code залишається _Cart__discount_code (НЕ перезаписано)\n",[3417,19619,19621],{"class":3419,"line":19620},191,[3417,19622,5877],{"class":3434},[3417,19624,19626],{"class":3419,"line":19625},192,[3417,19627,3452],{"emptyLinePlaceholder":3451},[3417,19629,19631,19634,19636],{"class":3419,"line":19630},193,[3417,19632,19633],{"class":3430},"    LOYALTY_DISCOUNT = ",[3417,19635,5598],{"class":3441},[3417,19637,19638],{"class":3423},"  # 5% додатково для VIP-клієнтів\n",[3417,19640,19642],{"class":3419,"line":19641},194,[3417,19643,3452],{"emptyLinePlaceholder":3451},[3417,19645,19647,19649,19651,19653,19655,19657,19659,19661,19663,19665,19668,19670,19672,19674,19676],{"class":3419,"line":19646},195,[3417,19648,3731],{"class":3718},[3417,19650,3735],{"class":3734},[3417,19652,3738],{"class":3430},[3417,19654,3742],{"class":3741},[3417,19656,3438],{"class":3430},[3417,19658,18167],{"class":3741},[3417,19660,3750],{"class":3430},[3417,19662,3876],{"class":3722},[3417,19664,3438],{"class":3430},[3417,19666,19667],{"class":3741},"loyalty_years",[3417,19669,3750],{"class":3430},[3417,19671,6030],{"class":3722},[3417,19673,10180],{"class":3430},[3417,19675,10099],{"class":3441},[3417,19677,3764],{"class":3430},[3417,19679,19681,19683,19685,19687],{"class":3419,"line":19680},196,[3417,19682,4793],{"class":3722},[3417,19684,4796],{"class":3430},[3417,19686,4799],{"class":3734},[3417,19688,19689],{"class":3430},"(owner_name)\n",[3417,19691,19693,19695,19698,19701],{"class":3419,"line":19692},197,[3417,19694,3769],{"class":3718},[3417,19696,19697],{"class":3430},".__discount_code = ",[3417,19699,19700],{"class":3434},"\"LOYALTY\"",[3417,19702,19703],{"class":3423},"  # → _PremiumCart__discount_code\n",[3417,19705,19707,19709],{"class":3419,"line":19706},198,[3417,19708,3769],{"class":3718},[3417,19710,19711],{"class":3430},"._loyalty_years  = loyalty_years\n",[3417,19713,19715],{"class":3419,"line":19714},199,[3417,19716,3452],{"emptyLinePlaceholder":3451},[3417,19718,19720,19722],{"class":3419,"line":19719},200,[3417,19721,5452],{"class":3734},[3417,19723,5455],{"class":3722},[3417,19725,19727,19729,19732,19734,19736,19738,19740],{"class":3419,"line":19726},201,[3417,19728,3731],{"class":3718},[3417,19730,19731],{"class":3734}," loyalty_discount",[3417,19733,3738],{"class":3430},[3417,19735,3742],{"class":3741},[3417,19737,3950],{"class":3430},[3417,19739,3536],{"class":3722},[3417,19741,3726],{"class":3430},[3417,19743,19745],{"class":3419,"line":19744},202,[3417,19746,19747],{"class":3434},"        \"\"\"Знижка за лояльністю: 5% × кількість років.\"\"\"\n",[3417,19749,19751,19753,19756,19758,19760,19763,19765,19768,19770],{"class":3419,"line":19750},203,[3417,19752,4130],{"class":3960},[3417,19754,19755],{"class":3734}," min",[3417,19757,3738],{"class":3430},[3417,19759,3742],{"class":3718},[3417,19761,19762],{"class":3430},".LOYALTY_DISCOUNT * ",[3417,19764,3742],{"class":3718},[3417,19766,19767],{"class":3430},"._loyalty_years, ",[3417,19769,6374],{"class":3441},[3417,19771,3445],{"class":3430},[3417,19773,19775],{"class":3419,"line":19774},204,[3417,19776,3452],{"emptyLinePlaceholder":3451},[3417,19778,19780,19782],{"class":3419,"line":19779},205,[3417,19781,5452],{"class":3734},[3417,19783,5455],{"class":3722},[3417,19785,19787,19789,19791,19793,19795,19797,19799],{"class":3419,"line":19786},206,[3417,19788,3731],{"class":3718},[3417,19790,18797],{"class":3734},[3417,19792,3738],{"class":3430},[3417,19794,3742],{"class":3741},[3417,19796,3950],{"class":3430},[3417,19798,3536],{"class":3722},[3417,19800,3726],{"class":3430},[3417,19802,19804],{"class":3419,"line":19803},207,[3417,19805,19806],{"class":3434},"        \"\"\"\n",[3417,19808,19810],{"class":3419,"line":19809},208,[3417,19811,19812],{"class":3434},"        Перевизначена властивість: знижка купона + знижка за лояльністю.\n",[3417,19814,19816],{"class":3419,"line":19815},209,[3417,19817,19818],{"class":3434},"        super().final_price використовує _Cart__discount_code (купон),\n",[3417,19820,19822],{"class":3419,"line":19821},210,[3417,19823,19824],{"class":3434},"        а self.loyalty_discount — з _PremiumCart__discount_code.\n",[3417,19826,19828],{"class":3419,"line":19827},211,[3417,19829,19806],{"class":3434},[3417,19831,19833,19836,19839],{"class":3419,"line":19832},212,[3417,19834,19835],{"class":3430},"        base = ",[3417,19837,19838],{"class":3722},"super",[3417,19840,19841],{"class":3430},"().final_price\n",[3417,19843,19845,19848,19851,19854,19856,19859,19861,19863,19865],{"class":3419,"line":19844},213,[3417,19846,19847],{"class":3430},"        loyalty_saving = ",[3417,19849,19850],{"class":3734},"round",[3417,19852,19853],{"class":3430},"(base * ",[3417,19855,3742],{"class":3718},[3417,19857,19858],{"class":3430},".loyalty_discount \u002F ",[3417,19860,9041],{"class":3441},[3417,19862,3438],{"class":3430},[3417,19864,18720],{"class":3441},[3417,19866,3445],{"class":3430},[3417,19868,19870,19872,19874,19877,19879],{"class":3419,"line":19869},214,[3417,19871,4130],{"class":3960},[3417,19873,18705],{"class":3734},[3417,19875,19876],{"class":3430},"(base - loyalty_saving, ",[3417,19878,18720],{"class":3441},[3417,19880,3445],{"class":3430},[3417,19882,19884],{"class":3419,"line":19883},215,[3417,19885,3452],{"emptyLinePlaceholder":3451},[3417,19887,19889,19891,19893,19895,19897,19899,19901],{"class":3419,"line":19888},216,[3417,19890,3731],{"class":3718},[3417,19892,9979],{"class":3734},[3417,19894,3738],{"class":3430},[3417,19896,3742],{"class":3741},[3417,19898,3950],{"class":3430},[3417,19900,3876],{"class":3722},[3417,19902,3726],{"class":3430},[3417,19904,19906,19908],{"class":3419,"line":19905},217,[3417,19907,4130],{"class":3960},[3417,19909,9996],{"class":3430},[3417,19911,19913,19915,19918,19920,19922,19924],{"class":3419,"line":19912},218,[3417,19914,10001],{"class":3718},[3417,19916,19917],{"class":3434},"\"PremiumCart(",[3417,19919,4744],{"class":3718},[3417,19921,19514],{"class":3430},[3417,19923,9861],{"class":3718},[3417,19925,10275],{"class":3434},[3417,19927,19929,19931,19934,19936,19939,19941],{"class":3419,"line":19928},219,[3417,19930,10001],{"class":3718},[3417,19932,19933],{"class":3434},"\"loyalty=",[3417,19935,4744],{"class":3718},[3417,19937,19938],{"class":3430},".loyalty_discount",[3417,19940,18465],{"class":3718},[3417,19942,19943],{"class":3434},"%, \"\n",[3417,19945,19947,19949,19951,19953,19955,19957],{"class":3419,"line":19946},220,[3417,19948,10001],{"class":3718},[3417,19950,19543],{"class":3434},[3417,19952,4744],{"class":3718},[3417,19954,19548],{"class":3430},[3417,19956,17537],{"class":3718},[3417,19958,19553],{"class":3434},[3417,19960,19962],{"class":3419,"line":19961},221,[3417,19963,10042],{"class":3430},[3408,19965,19967],{"className":3410,"code":19966,"filename":13589,"language":3412,"meta":3413,"style":3413},"from product import Product\nfrom cart import Cart, PremiumCart\n\nprint(\"=\" * 50)\nprint(\"  E-COMMERCE СИСТЕМА: ПОВНА ДЕМОНСТРАЦІЯ\")\nprint(\"=\" * 50)\n\n# ── 1. Каталог товарів ────────────────────────────────────────────────────────\nprint(\"\\n📦 Каталог товарів:\")\nlaptop  = Product(\"MacBook Pro 14\", price=89_999.0, stock=3, category=\"Ноутбуки\")\nphone   = Product(\"iPhone 15 Pro\",  price=44_999.0, stock=10, category=\"Смартфони\")\nearbuds = Product(\"AirPods Pro\",    price=9_999.0,  stock=25, category=\"Аксесуари\")\ncable   = Product(\"USB-C кабель\",   price=499.0,   stock=0,  category=\"Аксесуари\")\n\nfor p in [laptop, phone, earbuds, cable]:\n    print(f\"  {p}\")\n\n# ── 2. Валідація Product ──────────────────────────────────────────────────────\nprint(\"\\n🔒 Валідація Product:\")\nfor bad, label in [\n    (lambda: setattr(laptop, 'price', -1),    \"price=-1\"),\n    (lambda: setattr(laptop, 'stock', -5),    \"stock=-5 (int)\"),\n    (lambda: setattr(laptop, 'stock', 1.5),   \"stock=1.5 (float)\"),\n]:\n    try:\n        bad()\n    except (ValueError, TypeError) as e:\n        print(f\"  [{label}] {type(e).__name__}: {e}\")\n\n# ── 3. Звичайний Cart ─────────────────────────────────────────────────────────\nprint(\"\\n🛒 Звичайний кошик:\")\ncart = Cart(\"Іван Петренко\")\ncart.add(laptop, qty=1)\ncart.add(phone,  qty=2)\ncart.add(earbuds, qty=3)\n\nprint(f\"\\n  {cart}\")\nprint(f\"  subtotal:  {cart.subtotal:>10.2f} грн\")\nprint(f\"  ПДВ:       {cart.tax:>10.2f} грн\")\n\n# Купон\nprint(\"\\n🎟️  Купони:\")\ntry:\n    cart.coupon = \"FAKE99\"     # невалідний\nexcept ValueError as e:\n    print(f\"  ❌ {e}\")\n\ncart.coupon = \"SALE10\"        # валідний — 10%\nprint(f\"  Знижка: {cart.discount_amount:.2f} грн\")\nprint(f\"  До оплати: {cart.final_price:.2f} грн\")\n\ndel cart.coupon                # скасувати купон\nprint(f\"  Після скасування: {cart.final_price:.2f} грн\")\n\n# Checkout\ncart.coupon = \"VIP20\"\nprint(f\"\\n💳 Checkout (VIP20 — 20%):\")\nreceipt = cart.checkout()\nprint(receipt.to_text())\n\n# ── 4. Name Mangling: PremiumCart vs Cart ────────────────────────────────────\nprint(\"\\n👑 PremiumCart (Name Mangling демонстрація):\")\npremium = PremiumCart(\"Олена VIP\", loyalty_years=3)\npremium.add(laptop, qty=1)\npremium.add(earbuds, qty=2)\npremium.coupon = \"WELCOME5\"   # 5% купон (в _Cart__discount_code)\n\nprint(f\"\\n  {premium}\")\nprint(f\"  subtotal:           {premium.subtotal:.2f} грн\")\nprint(f\"  Купон (5%):        -{premium.discount_amount:.2f} грн\")\nprint(f\"  Лояльність (15%): після купона\")\nprint(f\"  final_price:        {premium.final_price:.2f} грн\")\n\n# Доводимо, що __discount_code різний у Cart та PremiumCart\nprint(f\"\\n  _Cart__discount_code:        \"\n      f\"{premium.__dict__.get('_Cart__discount_code')!r}\")\nprint(f\"  _PremiumCart__discount_code: \"\n      f\"{premium.__dict__.get('_PremiumCart__discount_code')!r}\")\nprint(f\"  (два різних поля — Name Mangling захищає від колізій!)\")\n\n# ── 5. Спроба замовити недоступний товар ────────────────────────────────────\nprint(\"\\n❌ Недоступний товар:\")\ntry:\n    Cart(\"Тест\").add(cable)\nexcept ValueError as e:\n    print(f\"  ValueError: {e}\")\n",[3399,19968,19969,19979,19991,19995,20009,20020,20034,20038,20043,20058,20096,20133,20171,20206,20210,20222,20242,20246,20251,20266,20278,20304,20329,20356,20361,20367,20372,20390,20431,20435,20440,20455,20465,20478,20491,20504,20508,20531,20553,20575,20579,20584,20599,20605,20616,20626,20647,20651,20662,20684,20706,20710,20720,20741,20745,20750,20757,20774,20779,20786,20790,20795,20810,20828,20841,20854,20865,20869,20892,20914,20936,20949,20971,20975,20980,20995,21023,21034,21059,21072,21076,21081,21096,21102,21113,21123],{"__ignoreMap":3413},[3417,19970,19971,19973,19975,19977],{"class":3419,"line":3420},[3417,19972,12959],{"class":3960},[3417,19974,17605],{"class":3430},[3417,19976,5375],{"class":3960},[3417,19978,17610],{"class":3430},[3417,19980,19981,19983,19986,19988],{"class":3419,"line":3427},[3417,19982,12959],{"class":3960},[3417,19984,19985],{"class":3430}," cart ",[3417,19987,5375],{"class":3960},[3417,19989,19990],{"class":3430}," Cart, PremiumCart\n",[3417,19992,19993],{"class":3419,"line":3448},[3417,19994,3452],{"emptyLinePlaceholder":3451},[3417,19996,19997,19999,20001,20003,20005,20007],{"class":3419,"line":3455},[3417,19998,4170],{"class":3734},[3417,20000,3738],{"class":3430},[3417,20002,17806],{"class":3434},[3417,20004,17809],{"class":3430},[3417,20006,9164],{"class":3441},[3417,20008,3445],{"class":3430},[3417,20010,20011,20013,20015,20018],{"class":3419,"line":3461},[3417,20012,4170],{"class":3734},[3417,20014,3738],{"class":3430},[3417,20016,20017],{"class":3434},"\"  E-COMMERCE СИСТЕМА: ПОВНА ДЕМОНСТРАЦІЯ\"",[3417,20019,3445],{"class":3430},[3417,20021,20022,20024,20026,20028,20030,20032],{"class":3419,"line":3473},[3417,20023,4170],{"class":3734},[3417,20025,3738],{"class":3430},[3417,20027,17806],{"class":3434},[3417,20029,17809],{"class":3430},[3417,20031,9164],{"class":3441},[3417,20033,3445],{"class":3430},[3417,20035,20036],{"class":3419,"line":3485},[3417,20037,3452],{"emptyLinePlaceholder":3451},[3417,20039,20040],{"class":3419,"line":3490},[3417,20041,20042],{"class":3423},"# ── 1. Каталог товарів ────────────────────────────────────────────────────────\n",[3417,20044,20045,20047,20049,20051,20053,20056],{"class":3419,"line":3496},[3417,20046,4170],{"class":3734},[3417,20048,3738],{"class":3430},[3417,20050,4015],{"class":3434},[3417,20052,6476],{"class":6475},[3417,20054,20055],{"class":3434},"📦 Каталог товарів:\"",[3417,20057,3445],{"class":3430},[3417,20059,20060,20063,20066,20068,20070,20072,20075,20077,20079,20081,20084,20086,20089,20091,20094],{"class":3419,"line":3645},[3417,20061,20062],{"class":3430},"laptop  = Product(",[3417,20064,20065],{"class":3434},"\"MacBook Pro 14\"",[3417,20067,3438],{"class":3430},[3417,20069,15326],{"class":3741},[3417,20071,7334],{"class":3430},[3417,20073,20074],{"class":3441},"89_999.0",[3417,20076,3438],{"class":3430},[3417,20078,15344],{"class":3741},[3417,20080,7334],{"class":3430},[3417,20082,20083],{"class":3441},"3",[3417,20085,3438],{"class":3430},[3417,20087,20088],{"class":3741},"category",[3417,20090,7334],{"class":3430},[3417,20092,20093],{"class":3434},"\"Ноутбуки\"",[3417,20095,3445],{"class":3430},[3417,20097,20098,20101,20104,20107,20109,20111,20114,20116,20118,20120,20122,20124,20126,20128,20131],{"class":3419,"line":3651},[3417,20099,20100],{"class":3430},"phone   = Product(",[3417,20102,20103],{"class":3434},"\"iPhone 15 Pro\"",[3417,20105,20106],{"class":3430},",  ",[3417,20108,15326],{"class":3741},[3417,20110,7334],{"class":3430},[3417,20112,20113],{"class":3441},"44_999.0",[3417,20115,3438],{"class":3430},[3417,20117,15344],{"class":3741},[3417,20119,7334],{"class":3430},[3417,20121,12879],{"class":3441},[3417,20123,3438],{"class":3430},[3417,20125,20088],{"class":3741},[3417,20127,7334],{"class":3430},[3417,20129,20130],{"class":3434},"\"Смартфони\"",[3417,20132,3445],{"class":3430},[3417,20134,20135,20138,20141,20144,20146,20148,20151,20153,20155,20157,20160,20162,20164,20166,20169],{"class":3419,"line":3657},[3417,20136,20137],{"class":3430},"earbuds = Product(",[3417,20139,20140],{"class":3434},"\"AirPods Pro\"",[3417,20142,20143],{"class":3430},",    ",[3417,20145,15326],{"class":3741},[3417,20147,7334],{"class":3430},[3417,20149,20150],{"class":3441},"9_999.0",[3417,20152,20106],{"class":3430},[3417,20154,15344],{"class":3741},[3417,20156,7334],{"class":3430},[3417,20158,20159],{"class":3441},"25",[3417,20161,3438],{"class":3430},[3417,20163,20088],{"class":3741},[3417,20165,7334],{"class":3430},[3417,20167,20168],{"class":3434},"\"Аксесуари\"",[3417,20170,3445],{"class":3430},[3417,20172,20173,20176,20179,20181,20183,20185,20188,20190,20192,20194,20196,20198,20200,20202,20204],{"class":3419,"line":3662},[3417,20174,20175],{"class":3430},"cable   = Product(",[3417,20177,20178],{"class":3434},"\"USB-C кабель\"",[3417,20180,9333],{"class":3430},[3417,20182,15326],{"class":3741},[3417,20184,7334],{"class":3430},[3417,20186,20187],{"class":3441},"499.0",[3417,20189,9333],{"class":3430},[3417,20191,15344],{"class":3741},[3417,20193,7334],{"class":3430},[3417,20195,3967],{"class":3441},[3417,20197,20106],{"class":3430},[3417,20199,20088],{"class":3741},[3417,20201,7334],{"class":3430},[3417,20203,20168],{"class":3434},[3417,20205,3445],{"class":3430},[3417,20207,20208],{"class":3419,"line":3668},[3417,20209,3452],{"emptyLinePlaceholder":3451},[3417,20211,20212,20214,20217,20219],{"class":3419,"line":3673},[3417,20213,10437],{"class":3960},[3417,20215,20216],{"class":3430}," p ",[3417,20218,10443],{"class":3960},[3417,20220,20221],{"class":3430}," [laptop, phone, earbuds, cable]:\n",[3417,20223,20224,20226,20228,20230,20232,20234,20236,20238,20240],{"class":3419,"line":3679},[3417,20225,4480],{"class":3734},[3417,20227,3738],{"class":3430},[3417,20229,4001],{"class":3718},[3417,20231,17851],{"class":3434},[3417,20233,4007],{"class":3718},[3417,20235,3395],{"class":3430},[3417,20237,4012],{"class":3718},[3417,20239,4015],{"class":3434},[3417,20241,3445],{"class":3430},[3417,20243,20244],{"class":3419,"line":3685},[3417,20245,3452],{"emptyLinePlaceholder":3451},[3417,20247,20248],{"class":3419,"line":4074},[3417,20249,20250],{"class":3423},"# ── 2. Валідація Product ──────────────────────────────────────────────────────\n",[3417,20252,20253,20255,20257,20259,20261,20264],{"class":3419,"line":4082},[3417,20254,4170],{"class":3734},[3417,20256,3738],{"class":3430},[3417,20258,4015],{"class":3434},[3417,20260,6476],{"class":6475},[3417,20262,20263],{"class":3434},"🔒 Валідація Product:\"",[3417,20265,3445],{"class":3430},[3417,20267,20268,20270,20273,20275],{"class":3419,"line":4104},[3417,20269,10437],{"class":3960},[3417,20271,20272],{"class":3430}," bad, label ",[3417,20274,10443],{"class":3960},[3417,20276,20277],{"class":3430}," [\n",[3417,20279,20280,20282,20284,20286,20288,20291,20293,20295,20297,20300,20302],{"class":3419,"line":4109},[3417,20281,10553],{"class":3430},[3417,20283,10556],{"class":3718},[3417,20285,3750],{"class":3430},[3417,20287,10561],{"class":3734},[3417,20289,20290],{"class":3430},"(laptop, ",[3417,20292,15577],{"class":3434},[3417,20294,10600],{"class":3430},[3417,20296,10099],{"class":3441},[3417,20298,20299],{"class":3430},"),    ",[3417,20301,15586],{"class":3434},[3417,20303,10581],{"class":3430},[3417,20305,20306,20308,20310,20312,20314,20316,20318,20320,20322,20324,20327],{"class":3419,"line":4127},[3417,20307,10553],{"class":3430},[3417,20309,10556],{"class":3718},[3417,20311,3750],{"class":3430},[3417,20313,10561],{"class":3734},[3417,20315,20290],{"class":3430},[3417,20317,15659],{"class":3434},[3417,20319,10600],{"class":3430},[3417,20321,6177],{"class":3441},[3417,20323,20299],{"class":3430},[3417,20325,20326],{"class":3434},"\"stock=-5 (int)\"",[3417,20328,10581],{"class":3430},[3417,20330,20331,20333,20335,20337,20339,20341,20343,20345,20348,20351,20354],{"class":3419,"line":4522},[3417,20332,10553],{"class":3430},[3417,20334,10556],{"class":3718},[3417,20336,3750],{"class":3430},[3417,20338,10561],{"class":3734},[3417,20340,20290],{"class":3430},[3417,20342,15659],{"class":3434},[3417,20344,3438],{"class":3430},[3417,20346,20347],{"class":3441},"1.5",[3417,20349,20350],{"class":3430},"),   ",[3417,20352,20353],{"class":3434},"\"stock=1.5 (float)\"",[3417,20355,10581],{"class":3430},[3417,20357,20358],{"class":3419,"line":4527},[3417,20359,20360],{"class":3430},"]:\n",[3417,20362,20363,20365],{"class":3419,"line":4533},[3417,20364,10669],{"class":3960},[3417,20366,3726],{"class":3430},[3417,20368,20369],{"class":3419,"line":4546},[3417,20370,20371],{"class":3430},"        bad()\n",[3417,20373,20374,20376,20378,20380,20382,20384,20386,20388],{"class":3419,"line":4551},[3417,20375,10683],{"class":3960},[3417,20377,6164],{"class":3430},[3417,20379,10688],{"class":3722},[3417,20381,3438],{"class":3430},[3417,20383,10693],{"class":3722},[3417,20385,10696],{"class":3430},[3417,20387,10699],{"class":3960},[3417,20389,4497],{"class":3430},[3417,20391,20392,20394,20396,20398,20401,20403,20405,20407,20409,20411,20413,20415,20417,20419,20421,20423,20425,20427,20429],{"class":3419,"line":4557},[3417,20393,4734],{"class":3734},[3417,20395,3738],{"class":3430},[3417,20397,4001],{"class":3718},[3417,20399,20400],{"class":3434},"\"  [",[3417,20402,4007],{"class":3718},[3417,20404,10718],{"class":3430},[3417,20406,4012],{"class":3718},[3417,20408,10723],{"class":3434},[3417,20410,4007],{"class":3718},[3417,20412,6056],{"class":3722},[3417,20414,10730],{"class":3430},[3417,20416,6062],{"class":3741},[3417,20418,4012],{"class":3718},[3417,20420,3750],{"class":3434},[3417,20422,4007],{"class":3718},[3417,20424,4513],{"class":3430},[3417,20426,4012],{"class":3718},[3417,20428,4015],{"class":3434},[3417,20430,3445],{"class":3430},[3417,20432,20433],{"class":3419,"line":4565},[3417,20434,3452],{"emptyLinePlaceholder":3451},[3417,20436,20437],{"class":3419,"line":4574},[3417,20438,20439],{"class":3423},"# ── 3. Звичайний Cart ─────────────────────────────────────────────────────────\n",[3417,20441,20442,20444,20446,20448,20450,20453],{"class":3419,"line":5657},[3417,20443,4170],{"class":3734},[3417,20445,3738],{"class":3430},[3417,20447,4015],{"class":3434},[3417,20449,6476],{"class":6475},[3417,20451,20452],{"class":3434},"🛒 Звичайний кошик:\"",[3417,20454,3445],{"class":3430},[3417,20456,20457,20460,20463],{"class":3419,"line":5680},[3417,20458,20459],{"class":3430},"cart = Cart(",[3417,20461,20462],{"class":3434},"\"Іван Петренко\"",[3417,20464,3445],{"class":3430},[3417,20466,20467,20470,20472,20474,20476],{"class":3419,"line":5685},[3417,20468,20469],{"class":3430},"cart.add(laptop, ",[3417,20471,17259],{"class":3741},[3417,20473,7334],{"class":3430},[3417,20475,10099],{"class":3441},[3417,20477,3445],{"class":3430},[3417,20479,20480,20483,20485,20487,20489],{"class":3419,"line":5691},[3417,20481,20482],{"class":3430},"cart.add(phone,  ",[3417,20484,17259],{"class":3741},[3417,20486,7334],{"class":3430},[3417,20488,18720],{"class":3441},[3417,20490,3445],{"class":3430},[3417,20492,20493,20496,20498,20500,20502],{"class":3419,"line":5700},[3417,20494,20495],{"class":3430},"cart.add(earbuds, ",[3417,20497,17259],{"class":3741},[3417,20499,7334],{"class":3430},[3417,20501,20083],{"class":3441},[3417,20503,3445],{"class":3430},[3417,20505,20506],{"class":3419,"line":5722},[3417,20507,3452],{"emptyLinePlaceholder":3451},[3417,20509,20510,20512,20514,20516,20518,20520,20522,20525,20527,20529],{"class":3419,"line":5727},[3417,20511,4170],{"class":3734},[3417,20513,3738],{"class":3430},[3417,20515,4001],{"class":3718},[3417,20517,4015],{"class":3434},[3417,20519,6476],{"class":6475},[3417,20521,15762],{"class":3718},[3417,20523,20524],{"class":3430},"cart",[3417,20526,4012],{"class":3718},[3417,20528,4015],{"class":3434},[3417,20530,3445],{"class":3430},[3417,20532,20533,20535,20537,20539,20542,20544,20547,20549,20551],{"class":3419,"line":5733},[3417,20534,4170],{"class":3734},[3417,20536,3738],{"class":3430},[3417,20538,4001],{"class":3718},[3417,20540,20541],{"class":3434},"\"  subtotal:  ",[3417,20543,4007],{"class":3718},[3417,20545,20546],{"class":3430},"cart.subtotal",[3417,20548,17915],{"class":3718},[3417,20550,17877],{"class":3434},[3417,20552,3445],{"class":3430},[3417,20554,20555,20557,20559,20561,20564,20566,20569,20571,20573],{"class":3419,"line":5740},[3417,20556,4170],{"class":3734},[3417,20558,3738],{"class":3430},[3417,20560,4001],{"class":3718},[3417,20562,20563],{"class":3434},"\"  ПДВ:       ",[3417,20565,4007],{"class":3718},[3417,20567,20568],{"class":3430},"cart.tax",[3417,20570,17915],{"class":3718},[3417,20572,17877],{"class":3434},[3417,20574,3445],{"class":3430},[3417,20576,20577],{"class":3419,"line":5749},[3417,20578,3452],{"emptyLinePlaceholder":3451},[3417,20580,20581],{"class":3419,"line":5760},[3417,20582,20583],{"class":3423},"# Купон\n",[3417,20585,20586,20588,20590,20592,20594,20597],{"class":3419,"line":6271},[3417,20587,4170],{"class":3734},[3417,20589,3738],{"class":3430},[3417,20591,4015],{"class":3434},[3417,20593,6476],{"class":6475},[3417,20595,20596],{"class":3434},"🎟️  Купони:\"",[3417,20598,3445],{"class":3430},[3417,20600,20601,20603],{"class":3419,"line":6277},[3417,20602,4473],{"class":3960},[3417,20604,3726],{"class":3430},[3417,20606,20607,20610,20613],{"class":3419,"line":6289},[3417,20608,20609],{"class":3430},"    cart.coupon = ",[3417,20611,20612],{"class":3434},"\"FAKE99\"",[3417,20614,20615],{"class":3423},"     # невалідний\n",[3417,20617,20618,20620,20622,20624],{"class":3419,"line":6299},[3417,20619,4488],{"class":3960},[3417,20621,3977],{"class":3722},[3417,20623,4494],{"class":3960},[3417,20625,4497],{"class":3430},[3417,20627,20628,20630,20632,20634,20637,20639,20641,20643,20645],{"class":3419,"line":6304},[3417,20629,4480],{"class":3734},[3417,20631,3738],{"class":3430},[3417,20633,4001],{"class":3718},[3417,20635,20636],{"class":3434},"\"  ❌ ",[3417,20638,4007],{"class":3718},[3417,20640,4513],{"class":3430},[3417,20642,4012],{"class":3718},[3417,20644,4015],{"class":3434},[3417,20646,3445],{"class":3430},[3417,20648,20649],{"class":3419,"line":6310},[3417,20650,3452],{"emptyLinePlaceholder":3451},[3417,20652,20653,20656,20659],{"class":3419,"line":6315},[3417,20654,20655],{"class":3430},"cart.coupon = ",[3417,20657,20658],{"class":3434},"\"SALE10\"",[3417,20660,20661],{"class":3423},"        # валідний — 10%\n",[3417,20663,20664,20666,20668,20670,20673,20675,20678,20680,20682],{"class":3419,"line":6322},[3417,20665,4170],{"class":3734},[3417,20667,3738],{"class":3430},[3417,20669,4001],{"class":3718},[3417,20671,20672],{"class":3434},"\"  Знижка: ",[3417,20674,4007],{"class":3718},[3417,20676,20677],{"class":3430},"cart.discount_amount",[3417,20679,17537],{"class":3718},[3417,20681,17877],{"class":3434},[3417,20683,3445],{"class":3430},[3417,20685,20686,20688,20690,20692,20695,20697,20700,20702,20704],{"class":3419,"line":6340},[3417,20687,4170],{"class":3734},[3417,20689,3738],{"class":3430},[3417,20691,4001],{"class":3718},[3417,20693,20694],{"class":3434},"\"  До оплати: ",[3417,20696,4007],{"class":3718},[3417,20698,20699],{"class":3430},"cart.final_price",[3417,20701,17537],{"class":3718},[3417,20703,17877],{"class":3434},[3417,20705,3445],{"class":3430},[3417,20707,20708],{"class":3419,"line":6346},[3417,20709,3452],{"emptyLinePlaceholder":3451},[3417,20711,20712,20714,20717],{"class":3419,"line":8484},[3417,20713,6627],{"class":3960},[3417,20715,20716],{"class":3430}," cart.coupon                ",[3417,20718,20719],{"class":3423},"# скасувати купон\n",[3417,20721,20722,20724,20726,20728,20731,20733,20735,20737,20739],{"class":3419,"line":8509},[3417,20723,4170],{"class":3734},[3417,20725,3738],{"class":3430},[3417,20727,4001],{"class":3718},[3417,20729,20730],{"class":3434},"\"  Після скасування: ",[3417,20732,4007],{"class":3718},[3417,20734,20699],{"class":3430},[3417,20736,17537],{"class":3718},[3417,20738,17877],{"class":3434},[3417,20740,3445],{"class":3430},[3417,20742,20743],{"class":3419,"line":8520},[3417,20744,3452],{"emptyLinePlaceholder":3451},[3417,20746,20747],{"class":3419,"line":8534},[3417,20748,20749],{"class":3423},"# Checkout\n",[3417,20751,20752,20754],{"class":3419,"line":8542},[3417,20753,20655],{"class":3430},[3417,20755,20756],{"class":3434},"\"VIP20\"\n",[3417,20758,20759,20761,20763,20765,20767,20769,20772],{"class":3419,"line":8547},[3417,20760,4170],{"class":3734},[3417,20762,3738],{"class":3430},[3417,20764,4001],{"class":3718},[3417,20766,4015],{"class":3434},[3417,20768,6476],{"class":6475},[3417,20770,20771],{"class":3434},"💳 Checkout (VIP20 — 20%):\"",[3417,20773,3445],{"class":3430},[3417,20775,20776],{"class":3419,"line":8552},[3417,20777,20778],{"class":3430},"receipt = cart.checkout()\n",[3417,20780,20781,20783],{"class":3419,"line":8563},[3417,20782,4170],{"class":3734},[3417,20784,20785],{"class":3430},"(receipt.to_text())\n",[3417,20787,20788],{"class":3419,"line":8574},[3417,20789,3452],{"emptyLinePlaceholder":3451},[3417,20791,20792],{"class":3419,"line":8583},[3417,20793,20794],{"class":3423},"# ── 4. Name Mangling: PremiumCart vs Cart ────────────────────────────────────\n",[3417,20796,20797,20799,20801,20803,20805,20808],{"class":3419,"line":8593},[3417,20798,4170],{"class":3734},[3417,20800,3738],{"class":3430},[3417,20802,4015],{"class":3434},[3417,20804,6476],{"class":6475},[3417,20806,20807],{"class":3434},"👑 PremiumCart (Name Mangling демонстрація):\"",[3417,20809,3445],{"class":3430},[3417,20811,20812,20815,20818,20820,20822,20824,20826],{"class":3419,"line":8598},[3417,20813,20814],{"class":3430},"premium = PremiumCart(",[3417,20816,20817],{"class":3434},"\"Олена VIP\"",[3417,20819,3438],{"class":3430},[3417,20821,19667],{"class":3741},[3417,20823,7334],{"class":3430},[3417,20825,20083],{"class":3441},[3417,20827,3445],{"class":3430},[3417,20829,20830,20833,20835,20837,20839],{"class":3419,"line":8605},[3417,20831,20832],{"class":3430},"premium.add(laptop, ",[3417,20834,17259],{"class":3741},[3417,20836,7334],{"class":3430},[3417,20838,10099],{"class":3441},[3417,20840,3445],{"class":3430},[3417,20842,20843,20846,20848,20850,20852],{"class":3419,"line":8613},[3417,20844,20845],{"class":3430},"premium.add(earbuds, ",[3417,20847,17259],{"class":3741},[3417,20849,7334],{"class":3430},[3417,20851,18720],{"class":3441},[3417,20853,3445],{"class":3430},[3417,20855,20856,20859,20862],{"class":3419,"line":8624},[3417,20857,20858],{"class":3430},"premium.coupon = ",[3417,20860,20861],{"class":3434},"\"WELCOME5\"",[3417,20863,20864],{"class":3423},"   # 5% купон (в _Cart__discount_code)\n",[3417,20866,20867],{"class":3419,"line":10278},[3417,20868,3452],{"emptyLinePlaceholder":3451},[3417,20870,20871,20873,20875,20877,20879,20881,20883,20886,20888,20890],{"class":3419,"line":10316},[3417,20872,4170],{"class":3734},[3417,20874,3738],{"class":3430},[3417,20876,4001],{"class":3718},[3417,20878,4015],{"class":3434},[3417,20880,6476],{"class":6475},[3417,20882,15762],{"class":3718},[3417,20884,20885],{"class":3430},"premium",[3417,20887,4012],{"class":3718},[3417,20889,4015],{"class":3434},[3417,20891,3445],{"class":3430},[3417,20893,20894,20896,20898,20900,20903,20905,20908,20910,20912],{"class":3419,"line":10321},[3417,20895,4170],{"class":3734},[3417,20897,3738],{"class":3430},[3417,20899,4001],{"class":3718},[3417,20901,20902],{"class":3434},"\"  subtotal:           ",[3417,20904,4007],{"class":3718},[3417,20906,20907],{"class":3430},"premium.subtotal",[3417,20909,17537],{"class":3718},[3417,20911,17877],{"class":3434},[3417,20913,3445],{"class":3430},[3417,20915,20916,20918,20920,20922,20925,20927,20930,20932,20934],{"class":3419,"line":10326},[3417,20917,4170],{"class":3734},[3417,20919,3738],{"class":3430},[3417,20921,4001],{"class":3718},[3417,20923,20924],{"class":3434},"\"  Купон (5%):        -",[3417,20926,4007],{"class":3718},[3417,20928,20929],{"class":3430},"premium.discount_amount",[3417,20931,17537],{"class":3718},[3417,20933,17877],{"class":3434},[3417,20935,3445],{"class":3430},[3417,20937,20938,20940,20942,20944,20947],{"class":3419,"line":10331},[3417,20939,4170],{"class":3734},[3417,20941,3738],{"class":3430},[3417,20943,4001],{"class":3718},[3417,20945,20946],{"class":3434},"\"  Лояльність (15%): після купона\"",[3417,20948,3445],{"class":3430},[3417,20950,20951,20953,20955,20957,20960,20962,20965,20967,20969],{"class":3419,"line":10337},[3417,20952,4170],{"class":3734},[3417,20954,3738],{"class":3430},[3417,20956,4001],{"class":3718},[3417,20958,20959],{"class":3434},"\"  final_price:        ",[3417,20961,4007],{"class":3718},[3417,20963,20964],{"class":3430},"premium.final_price",[3417,20966,17537],{"class":3718},[3417,20968,17877],{"class":3434},[3417,20970,3445],{"class":3430},[3417,20972,20973],{"class":3419,"line":10360},[3417,20974,3452],{"emptyLinePlaceholder":3451},[3417,20976,20977],{"class":3419,"line":10368},[3417,20978,20979],{"class":3423},"# Доводимо, що __discount_code різний у Cart та PremiumCart\n",[3417,20981,20982,20984,20986,20988,20990,20992],{"class":3419,"line":10373},[3417,20983,4170],{"class":3734},[3417,20985,3738],{"class":3430},[3417,20987,4001],{"class":3718},[3417,20989,4015],{"class":3434},[3417,20991,6476],{"class":6475},[3417,20993,20994],{"class":3434},"  _Cart__discount_code:        \"\n",[3417,20996,20997,21000,21002,21004,21007,21009,21012,21015,21017,21019,21021],{"class":3419,"line":10382},[3417,20998,20999],{"class":3718},"      f",[3417,21001,4015],{"class":3434},[3417,21003,4007],{"class":3718},[3417,21005,21006],{"class":3430},"premium.",[3417,21008,4541],{"class":3741},[3417,21010,21011],{"class":3430},".get(",[3417,21013,21014],{"class":3434},"'_Cart__discount_code'",[3417,21016,10775],{"class":3430},[3417,21018,9861],{"class":3718},[3417,21020,4015],{"class":3434},[3417,21022,3445],{"class":3430},[3417,21024,21025,21027,21029,21031],{"class":3419,"line":10405},[3417,21026,4170],{"class":3734},[3417,21028,3738],{"class":3430},[3417,21030,4001],{"class":3718},[3417,21032,21033],{"class":3434},"\"  _PremiumCart__discount_code: \"\n",[3417,21035,21036,21038,21040,21042,21044,21046,21048,21051,21053,21055,21057],{"class":3419,"line":10410},[3417,21037,20999],{"class":3718},[3417,21039,4015],{"class":3434},[3417,21041,4007],{"class":3718},[3417,21043,21006],{"class":3430},[3417,21045,4541],{"class":3741},[3417,21047,21011],{"class":3430},[3417,21049,21050],{"class":3434},"'_PremiumCart__discount_code'",[3417,21052,10775],{"class":3430},[3417,21054,9861],{"class":3718},[3417,21056,4015],{"class":3434},[3417,21058,3445],{"class":3430},[3417,21060,21061,21063,21065,21067,21070],{"class":3419,"line":10416},[3417,21062,4170],{"class":3734},[3417,21064,3738],{"class":3430},[3417,21066,4001],{"class":3718},[3417,21068,21069],{"class":3434},"\"  (два різних поля — Name Mangling захищає від колізій!)\"",[3417,21071,3445],{"class":3430},[3417,21073,21074],{"class":3419,"line":10470},[3417,21075,3452],{"emptyLinePlaceholder":3451},[3417,21077,21078],{"class":3419,"line":10495},[3417,21079,21080],{"class":3423},"# ── 5. Спроба замовити недоступний товар ────────────────────────────────────\n",[3417,21082,21083,21085,21087,21089,21091,21094],{"class":3419,"line":10500},[3417,21084,4170],{"class":3734},[3417,21086,3738],{"class":3430},[3417,21088,4015],{"class":3434},[3417,21090,6476],{"class":6475},[3417,21092,21093],{"class":3434},"❌ Недоступний товар:\"",[3417,21095,3445],{"class":3430},[3417,21097,21098,21100],{"class":3419,"line":10506},[3417,21099,4473],{"class":3960},[3417,21101,3726],{"class":3430},[3417,21103,21104,21107,21110],{"class":3419,"line":10533},[3417,21105,21106],{"class":3430},"    Cart(",[3417,21108,21109],{"class":3434},"\"Тест\"",[3417,21111,21112],{"class":3430},").add(cable)\n",[3417,21114,21115,21117,21119,21121],{"class":3419,"line":10538},[3417,21116,4488],{"class":3960},[3417,21118,3977],{"class":3722},[3417,21120,4494],{"class":3960},[3417,21122,4497],{"class":3430},[3417,21124,21125,21127,21129,21131,21134,21136,21138,21140,21142],{"class":3419,"line":10544},[3417,21126,4480],{"class":3734},[3417,21128,3738],{"class":3430},[3417,21130,4001],{"class":3718},[3417,21132,21133],{"class":3434},"\"  ValueError: ",[3417,21135,4007],{"class":3718},[3417,21137,4513],{"class":3430},[3417,21139,4012],{"class":3718},[3417,21141,4015],{"class":3434},[3417,21143,3445],{"class":3430},[4189,21145,21146,21154,21158,21162,21165,21168,21172,21189,21204,21220,21236,21239,21243,21251,21259,21267,21270,21274,21282,21289,21297,21306,21314,21322,21325,21329,21337,21349,21357,21365,21369,21372,21376,21380,21384,21387,21394,21401,21408,21412,21420,21427,21435,21438,21446,21449,21452,21456,21464,21476,21479,21490,21503,21510,21513,21517],{"title":14018},[4193,21147,21149,4201,21152],{"className":21148},[3419],[3417,21150,4200],{"className":21151},[4199],[3516,21153,14018],{},[4193,21155,21157],{"className":21156},[3419],"==================================================",[4193,21159,21161],{"className":21160},[3419],"  E-COMMERCE СИСТЕМА: ПОВНА ДЕМОНСТРАЦІЯ",[4193,21163,21157],{"className":21164},[3419],[4193,21166],{"className":21167},[3419],[4193,21169,21171],{"className":21170},[3419],"📦 Каталог товарів:",[4193,21173,21175,21176,15427,21180,21184,21185,21188],{"className":21174},[3419],"  Product(",[3417,21177,21179],{"className":21178},[4607],"'MacBook Pro 14'",[3417,21181,21183],{"className":21182},[4607],"89999.00"," грн, stock=",[3417,21186,20083],{"className":21187},[4607]," ✅)",[4193,21190,21175,21192,21196,21197,21184,21201,21188],{"className":21191},[3419],[3417,21193,21195],{"className":21194},[4607],"'iPhone 15 Pro'",",  price=",[3417,21198,21200],{"className":21199},[4607],"44999.00",[3417,21202,12879],{"className":21203},[4607],[4193,21205,21175,21207,21211,21212,21216,21217,21188],{"className":21206},[3419],[3417,21208,21210],{"className":21209},[4607],"'AirPods Pro'",",    price=",[3417,21213,21215],{"className":21214},[4607],"9999.00","  грн, stock=",[3417,21218,20159],{"className":21219},[4607],[4193,21221,21175,21223,21227,21228,21216,21232,21235],{"className":21222},[3419],[3417,21224,21226],{"className":21225},[4607],"'USB-C кабель'",",   price=",[3417,21229,21231],{"className":21230},[4607],"499.00",[3417,21233,3967],{"className":21234},[4598],"  ❌)",[4193,21237],{"className":21238},[3419],[4193,21240,21242],{"className":21241},[3419],"🔒 Валідація Product:",[4193,21244,21246,21247],{"className":21245},[3419],"  [price=-1]   ",[3417,21248,21250],{"className":21249},[4598],"ValueError: 'price' має бути > 0, отримано -1",[4193,21252,21254,21255],{"className":21253},[3419],"  [stock=-5]   ",[3417,21256,21258],{"className":21257},[4598],"ValueError: 'stock' має бути >= 0, отримано -5",[4193,21260,21262,21263],{"className":21261},[3419],"  [stock=1.5]  ",[3417,21264,21266],{"className":21265},[4598],"TypeError: 'stock' має бути int, отримано 'float'",[4193,21268],{"className":21269},[3419],[4193,21271,21273],{"className":21272},[3419],"🛒 Звичайний кошик:",[4193,21275,21277,21278,21281],{"className":21276},[3419],"  + MacBook Pro 14 × 1  (",[3417,21279,21183],{"className":21280},[4607]," грн\u002Fшт)",[4193,21283,21285,21286,21281],{"className":21284},[3419],"  + iPhone 15 Pro  × 2  (",[3417,21287,21200],{"className":21288},[4607],[4193,21290,21292,21293,21296],{"className":21291},[3419],"  + AirPods Pro    × 3  (",[3417,21294,21215],{"className":21295},[4607],"  грн\u002Fшт)",[4193,21298,21300,21301,21305],{"className":21299},[3419],"  Cart('Іван Петренко', items=6, total=",[3417,21302,21304],{"className":21303},[4624],"209993.00"," грн)",[4193,21307,21309,21310,21313],{"className":21308},[3419],"  subtotal:   ",[3417,21311,21304],{"className":21312},[4607]," грн",[4193,21315,21317,21318,21313],{"className":21316},[3419],"  ПДВ:         ",[3417,21319,21321],{"className":21320},[4212],"34998.83",[4193,21323],{"className":21324},[3419],[4193,21326,21328],{"className":21327},[3419],"🎟️  Купони:",[4193,21330,21332,21333],{"className":21331},[3419],"  ❌ ",[3417,21334,21336],{"className":21335},[4598],"Купон 'FAKE99' не знайдено або вже не діє.",[4193,21338,21340,21341,18457,21345,21348],{"className":21339},[3419],"  ✅ Купон ",[3417,21342,21344],{"className":21343},[4607],"'SALE10'",[3417,21346,12879],{"className":21347},[4607],"%",[4193,21350,21352,21353,21313],{"className":21351},[3419],"  Знижка:      ",[3417,21354,21356],{"className":21355},[4607],"20999.30",[4193,21358,21360,21361,21313],{"className":21359},[3419],"  До оплати:  ",[3417,21362,21364],{"className":21363},[4607],"188993.70",[4193,21366,21368],{"className":21367},[3419],"  🗑️  Купон 'SALE10' скасовано",[4193,21370],{"className":21371},[3419],[4193,21373,21375],{"className":21374},[3419],"💳 Checkout (VIP20 — 20%):",[4193,21377,21379],{"className":21378},[3419],"==========================================",[4193,21381,21383],{"className":21382},[3419],"        ЧАРТЕР ЗАМОВЛЕННЯ",[4193,21385,21379],{"className":21386},[3419],[4193,21388,21390,21391,21313],{"className":21389},[3419],"  MacBook Pro 14       1 ×  ",[3417,21392,21183],{"className":21393},[4607],[4193,21395,21397,21398,21313],{"className":21396},[3419],"  iPhone 15 Pro        2 ×  ",[3417,21399,21200],{"className":21400},[4607],[4193,21402,21404,21405,21313],{"className":21403},[3419],"  AirPods Pro          3 ×   ",[3417,21406,21215],{"className":21407},[4607],[4193,21409,21411],{"className":21410},[3419],"------------------------------------------",[4193,21413,21415,21416,21313],{"className":21414},[3419],"  Сума без ПДВ:      ",[3417,21417,21419],{"className":21418},[4624],"174994.17",[4193,21421,21423,21424,21313],{"className":21422},[3419],"  ПДВ (20%):          ",[3417,21425,21321],{"className":21426},[4212],[4193,21428,21430,21431,21313],{"className":21429},[3419],"  Знижка (VIP20):    ",[3417,21432,21434],{"className":21433},[4607],"-41998.60",[4193,21436,21379],{"className":21437},[3419],[4193,21439,21441,21442,21313],{"className":21440},[3419],"  РАЗОМ:             ",[3417,21443,21445],{"className":21444},[4607],"167994.40",[4193,21447,21379],{"className":21448},[3419],[4193,21450],{"className":21451},[3419],[4193,21453,21455],{"className":21454},[3419],"👑 PremiumCart (Name Mangling демонстрація):",[4193,21457,21340,21459,21463],{"className":21458},[3419],[3417,21460,21462],{"className":21461},[4607],"'WELCOME5'"," активовано: знижка 5%",[4193,21465,21467,21468,21471,21472,21305],{"className":21466},[3419],"  PremiumCart('Олена VIP', loyalty=",[3417,21469,12891],{"className":21470},[4624],"%, total=",[3417,21473,21475],{"className":21474},[4607],"93894.37",[4193,21477],{"className":21478},[3419],[4193,21480,21482,21483,4625,21486],{"className":21481},[3419],"  _Cart__discount_code:        ",[3417,21484,21462],{"className":21485},[4212],[3417,21487,21489],{"className":21488},[4218],"# купон (з Cart)",[4193,21491,21493,21494,21498,21499],{"className":21492},[3419],"  _PremiumCart__discount_code: ",[3417,21495,21497],{"className":21496},[4624],"'LOYALTY'","   ",[3417,21500,21502],{"className":21501},[4218],"# loyalty (з PremiumCart)",[4193,21504,4625,21506],{"className":21505},[3419],[3417,21507,21509],{"className":21508},[4607],"(два різних поля — Name Mangling захищає від колізій!)",[4193,21511],{"className":21512},[3419],[4193,21514,21516],{"className":21515},[3419],"❌ Недоступний товар:",[4193,21518,21520,21521],{"className":21519},[3419],"  ValueError: ",[3417,21522,21524],{"className":21523},[4598],"Товар 'USB-C кабель' недоступний",[3553,21526],{},[3390,21528,21530],{"id":21529},"підсумки-та-найкращі-практики","Підсумки та найкращі практики",[3521,21532,21533,21541,21553,21575,21587,21598],{},[3524,21534,21537,21538,21540],{"icon":21535,"title":21536},"i-heroicons-arrow-trending-up","Починайте просто","Починайте з публічних атрибутів. Переходьте до ",[3399,21539,5086],{}," лише коли з'являється реальна потреба в валідації або обчисленні. Рання оптимізація — корінь усього зла.",[3524,21542,21544,21545,21548,21549,21552],{"icon":3526,"title":21543},"Угода _protected","Для більшості випадків ",[3399,21546,21547],{},"_single_underscore"," є достатнім. ",[3399,21550,21551],{},"__double_underscore"," (Name Mangling) застосовуйте виключно у базових класах бібліотек для захисту від колізій у підкласах.",[3524,21554,21557,21558,5841,21561,21564,21565,21567,21568,21571,21572,3589],{"icon":21555,"title":21556},"i-heroicons-check-circle","Pythonic @property","Не пишіть ",[3399,21559,21560],{},"get_balance()",[3399,21562,21563],{},"set_balance()",". Використовуйте ",[3399,21566,5086],{}," — зовнішній код буде мати синтаксис ",[3399,21569,21570],{},"account.balance = 1000"," замість ",[3399,21573,21574],{},"account.set_balance(1000)",[3524,21576,21579,21580,21582,21583,21586],{"icon":21577,"title":21578},"i-heroicons-squares-2x2","DRY з дескрипторами","Якщо одна й та сама валідаційна логіка потрібна у 3+ атрибутах — виносьте у дескриптор. Один раз написати ",[3399,21581,9517],{}," або ",[3399,21584,21585],{},"NonEmptyString"," і використовувати декларативно.",[3524,21588,21591,21593,21594,21597],{"icon":21589,"title":21590},"i-heroicons-bolt","Продуктивність",[3399,21592,5086],{}," додає незначний overhead (~50–100 нс). Не варто оптимізувати передчасно. Але якщо властивість виконує важкий запит — оформіть її як метод ",[3399,21595,21596],{},"get_data()",", щоб виклик був явним.",[3524,21599,21602,21603,5841,21606,21609],{"icon":21600,"title":21601},"i-heroicons-user-group","Consenting Adults","Python не забороняє доступ до ",[3399,21604,21605],{},"_private",[3399,21607,21608],{},"__mangled",". Поважайте чужі угоди та не лізьте під капот сторонніх об'єктів без крайньої потреби. Кожне порушення — ваш технічний борг.",[21611,21612,21613],"style",{},"html pre.shiki code .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}html pre.shiki code .sHH4Y, html code.shiki .sHH4Y{--shiki-light:#000000;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sJj4R, html code.shiki .sJj4R{--shiki-light:#098658;--shiki-default:#B5CEA8;--shiki-dark:#B5CEA8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .su1O8, html code.shiki .su1O8{--shiki-light:#0000FF;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sN1BT, html code.shiki .sN1BT{--shiki-light:#267F99;--shiki-default:#4EC9B0;--shiki-dark:#4EC9B0}html pre.shiki code .s8Opu, html code.shiki .s8Opu{--shiki-light:#795E26;--shiki-default:#DCDCAA;--shiki-dark:#DCDCAA}html pre.shiki code .siwwj, html code.shiki .siwwj{--shiki-light:#001080;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .s8xlr, html code.shiki .s8xlr{--shiki-light:#AF00DB;--shiki-default:#C586C0;--shiki-dark:#C586C0}html pre.shiki code .sjcCO, html code.shiki .sjcCO{--shiki-light:#EE0000;--shiki-default:#D7BA7D;--shiki-dark:#D7BA7D}html pre.shiki code .shsrj, html code.shiki .shsrj{--shiki-light:#811F3F;--shiki-default:#D16969;--shiki-dark:#D16969}html pre.shiki code .sAV2Q, html code.shiki .sAV2Q{--shiki-light:#D16969;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sYsli, html code.shiki .sYsli{--shiki-light:#000000;--shiki-default:#D7BA7D;--shiki-dark:#D7BA7D}",{"title":3413,"searchDepth":3427,"depth":3427,"links":21615},[21616,21617,21620,21630,21640,21656,21664,21669,21673],{"id":3392,"depth":3427,"text":3393},{"id":3557,"depth":3427,"text":3558,"children":21618},[21619],{"id":3562,"depth":3448,"text":3563},{"id":3693,"depth":3427,"text":3694,"children":21621},[21622,21624,21626,21628,21629],{"id":3697,"depth":3448,"text":21623},"public — за замовчуванням",{"id":3818,"depth":3448,"text":21625},"_protected — угода про внутрішнє використання",{"id":4264,"depth":3448,"text":21627},"__private — Name Mangling",{"id":4660,"depth":3448,"text":4661},{"id":4969,"depth":3448,"text":4970},{"id":5082,"depth":3427,"text":21631,"children":21632},"Частина III: @property — Pythonic-шлях до валідації",[21633,21634,21636,21637,21638],{"id":5090,"depth":3448,"text":5091},{"id":5347,"depth":3448,"text":21635},"Декоратор @property: обчислювальні властивості (read-only)",{"id":5833,"depth":3448,"text":5834},{"id":6764,"depth":3448,"text":6765},{"id":7014,"depth":3448,"text":21639},"Декоратори vs функція property()",{"id":7406,"depth":3427,"text":7407,"children":21641},[21642,21643,21644,21645,21646,21648,21649,21651,21652,21654],{"id":7410,"depth":3448,"text":7411},{"id":7476,"depth":3448,"text":7477},{"id":7497,"depth":3448,"text":7498},{"id":7512,"depth":3448,"text":7513},{"id":7544,"depth":3448,"text":21647},"Крок 3: Пошук у obj.__dict__",{"id":7555,"depth":3448,"text":7556},{"id":7577,"depth":3448,"text":21650},"Крок 5: __getattr__",{"id":7783,"depth":3448,"text":7784},{"id":7930,"depth":3448,"text":21653},"Симуляція property на чистому Python",{"id":8679,"depth":3448,"text":21655},"Незмінність об'єктів property: чому .setter створює новий об'єкт",{"id":8908,"depth":3427,"text":8909,"children":21657},[21658,21660,21662],{"id":8912,"depth":3448,"text":21659},"Проблема: десятки однакових @property",{"id":9197,"depth":3448,"text":21661},"__set_name__: автоматичне отримання імені атрибута (Python 3.6+)",{"id":9513,"depth":3448,"text":21663},"Повна реалізація IntegerRange дескриптора",{"id":10880,"depth":3427,"text":10881,"children":21665},[21666,21667,21668],{"id":10884,"depth":3448,"text":10885},{"id":11860,"depth":3448,"text":11861},{"id":14129,"depth":3448,"text":14130},{"id":15874,"depth":3427,"text":15875,"children":21670},[21671,21672],{"id":16003,"depth":3448,"text":16004},{"id":16321,"depth":3448,"text":16322},{"id":21529,"depth":3427,"text":21530},"Глибоке дослідження інкапсуляції в Python — від філософії «дорослих людей» та угод про іменування до механізму Name Mangling, обчислювальних властивостей з @property, кастомних дескрипторів та валідації даних через геттери й сеттери.","md",null,{},{"title":2561,"description":21674},"nDBePN1EYZOzRTBrKbjtPFNY39sxYk5dV7w-dQ-5Zpc",[21681,21683],{"title":71,"path":2558,"stem":2559,"description":21682,"children":-1},"Глибоке дослідження механізму класів і об'єктів у Python — від фундаментальних принципів інстанціювання до CPython internals, різниці між __init__ та __new__, оптимізації пам'яті через __slots__ та природи self як неявного першого аргументу.",{"title":2565,"path":2566,"stem":2567,"description":21684,"children":-1},"Глибоке дослідження механізмів наслідування в Python — від одиночного наслідування та перевизначення методів до cooperative multiple inheritance, внутрішнього влаштування super() як проксі-об'єкта, детального математичного аналізу C3-лінеаризації, CPython Internals MRO та патерну Mixins.",1783248143911]