[{"data":1,"prerenderedAt":14482},["ShallowReactive",2],{"navigation_docs":3,"-python-descriptors":3379,"-python-descriptors-surround":14477},[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":2581,"body":3381,"description":14471,"extension":14472,"links":14473,"meta":14474,"navigation":3490,"path":2582,"seo":14475,"stem":2583,"__hash__":14476},"docs\u002F05.python\u002F07.descriptors.md",{"type":3382,"value":3383,"toc":14428},"minimark",[3384,3388,3393,3406,3541,3551,3792,3808,3834,3842,3845,3849,3854,3875,3878,3892,3895,3921,3928,3932,3935,4012,4016,4019,4644,4723,4726,4739,4764,4774,4792,4800,4810,4812,4816,4824,4835,4842,4950,5177,5181,5184,5284,5287,5833,5919,5930,5934,5950,6188,6207,6214,6219,6522,6534,6536,6540,6544,6547,6550,6844,6850,7909,7913,7916,8697,8755,8759,8766,8789,8803,8817,8819,8823,8827,8834,8840,8847,8867,8871,8890,8920,8923,9763,9856,10185,10299,10306,10316,10721,10740,10744,10756,10859,10875,10877,10881,10885,10888,10891,12622,12626,12632,13526,13530,14079,14217,14221,14224,14262,14265,14267,14271,14274,14313,14317,14424],[3385,3386,2581],"h1",{"id":3387},"дескриптори-магія-доступу-до-атрибутів",[3389,3390,3392],"h2",{"id":3391},"проблема-атрибути-поводяться-не-так-як-ви-очікуєте","Проблема: атрибути поводяться не так, як ви очікуєте",[3394,3395,3396,3397,3401,3402,3405],"p",{},"Уявіть, що ви розробляєте систему для зберігання даних фізичних вимірювань. У вас є клас ",[3398,3399,3400],"code",{},"Sensor",", що зберігає показники температури. Все просто на перший погляд: встановив атрибут ",[3398,3403,3404],{},"temperature"," — і готово.",[3407,3408,3413],"pre",{"className":3409,"code":3410,"language":3411,"meta":3412,"style":3412},"language-python shiki shiki-themes light-plus dark-plus dark-plus","class Sensor:\n    def __init__(self, name: str):\n        self.name = name\n        self.temperature = 0.0\n\ns = Sensor(\"Кімнатний\")\ns.temperature = 22.5   # нормально\ns.temperature = -500   # фізично неможливо, але Python мовчить\ns.temperature = \"дуже гаряче\"  # взагалі рядок — і знову тиша\n","python","",[3398,3414,3415,3432,3464,3473,3485,3492,3505,3518,3530],{"__ignoreMap":3412},[3416,3417,3420,3424,3428],"span",{"class":3418,"line":3419},"line",1,[3416,3421,3423],{"class":3422},"su1O8","class",[3416,3425,3427],{"class":3426},"sN1BT"," Sensor",[3416,3429,3431],{"class":3430},"sHH4Y",":\n",[3416,3433,3435,3438,3442,3445,3449,3452,3455,3458,3461],{"class":3418,"line":3434},2,[3416,3436,3437],{"class":3422},"    def",[3416,3439,3441],{"class":3440},"s8Opu"," __init__",[3416,3443,3444],{"class":3430},"(",[3416,3446,3448],{"class":3447},"siwwj","self",[3416,3450,3451],{"class":3430},", ",[3416,3453,3454],{"class":3447},"name",[3416,3456,3457],{"class":3430},": ",[3416,3459,3460],{"class":3426},"str",[3416,3462,3463],{"class":3430},"):\n",[3416,3465,3467,3470],{"class":3418,"line":3466},3,[3416,3468,3469],{"class":3422},"        self",[3416,3471,3472],{"class":3430},".name = name\n",[3416,3474,3476,3478,3481],{"class":3418,"line":3475},4,[3416,3477,3469],{"class":3422},[3416,3479,3480],{"class":3430},".temperature = ",[3416,3482,3484],{"class":3483},"sJj4R","0.0\n",[3416,3486,3488],{"class":3418,"line":3487},5,[3416,3489,3491],{"emptyLinePlaceholder":3490},true,"\n",[3416,3493,3495,3498,3502],{"class":3418,"line":3494},6,[3416,3496,3497],{"class":3430},"s = Sensor(",[3416,3499,3501],{"class":3500},"sbdoH","\"Кімнатний\"",[3416,3503,3504],{"class":3430},")\n",[3416,3506,3508,3511,3514],{"class":3418,"line":3507},7,[3416,3509,3510],{"class":3430},"s.temperature = ",[3416,3512,3513],{"class":3483},"22.5",[3416,3515,3517],{"class":3516},"spJ8K","   # нормально\n",[3416,3519,3521,3524,3527],{"class":3418,"line":3520},8,[3416,3522,3523],{"class":3430},"s.temperature = -",[3416,3525,3526],{"class":3483},"500",[3416,3528,3529],{"class":3516},"   # фізично неможливо, але Python мовчить\n",[3416,3531,3533,3535,3538],{"class":3418,"line":3532},9,[3416,3534,3510],{"class":3430},[3416,3536,3537],{"class":3500},"\"дуже гаряче\"",[3416,3539,3540],{"class":3516},"  # взагалі рядок — і знову тиша\n",[3394,3542,3543,3544,3546,3547,3550],{},"Атрибут ",[3398,3545,3404],{}," є звичайним «слотом для зберігання» — Python нічого не перевіряє і нічого не знає про фізичну природу вашого поля. Щоб додати валідацію, починаючий розробник інстинктивно сягає за ",[3398,3548,3549],{},"@property",":",[3407,3552,3554],{"className":3409,"code":3553,"language":3411,"meta":3412,"style":3412},"class Sensor:\n    def __init__(self, name: str):\n        self.name = name\n        self._temperature = 0.0\n\n    @property\n    def temperature(self) -> float:\n        return self._temperature\n\n    @temperature.setter\n    def temperature(self, value: float) -> None:\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 нижча за абсолютний нуль\")\n        self._temperature = float(value)\n",[3398,3555,3556,3564,3584,3590,3599,3603,3611,3630,3642,3646,3652,3679,3704,3741,3754,3780],{"__ignoreMap":3412},[3416,3557,3558,3560,3562],{"class":3418,"line":3419},[3416,3559,3423],{"class":3422},[3416,3561,3427],{"class":3426},[3416,3563,3431],{"class":3430},[3416,3565,3566,3568,3570,3572,3574,3576,3578,3580,3582],{"class":3418,"line":3434},[3416,3567,3437],{"class":3422},[3416,3569,3441],{"class":3440},[3416,3571,3444],{"class":3430},[3416,3573,3448],{"class":3447},[3416,3575,3451],{"class":3430},[3416,3577,3454],{"class":3447},[3416,3579,3457],{"class":3430},[3416,3581,3460],{"class":3426},[3416,3583,3463],{"class":3430},[3416,3585,3586,3588],{"class":3418,"line":3466},[3416,3587,3469],{"class":3422},[3416,3589,3472],{"class":3430},[3416,3591,3592,3594,3597],{"class":3418,"line":3475},[3416,3593,3469],{"class":3422},[3416,3595,3596],{"class":3430},"._temperature = ",[3416,3598,3484],{"class":3483},[3416,3600,3601],{"class":3418,"line":3487},[3416,3602,3491],{"emptyLinePlaceholder":3490},[3416,3604,3605,3608],{"class":3418,"line":3494},[3416,3606,3607],{"class":3440},"    @",[3416,3609,3610],{"class":3426},"property\n",[3416,3612,3613,3615,3618,3620,3622,3625,3628],{"class":3418,"line":3507},[3416,3614,3437],{"class":3422},[3416,3616,3617],{"class":3440}," temperature",[3416,3619,3444],{"class":3430},[3416,3621,3448],{"class":3447},[3416,3623,3624],{"class":3430},") -> ",[3416,3626,3627],{"class":3426},"float",[3416,3629,3431],{"class":3430},[3416,3631,3632,3636,3639],{"class":3418,"line":3520},[3416,3633,3635],{"class":3634},"s8xlr","        return",[3416,3637,3638],{"class":3422}," self",[3416,3640,3641],{"class":3430},"._temperature\n",[3416,3643,3644],{"class":3418,"line":3532},[3416,3645,3491],{"emptyLinePlaceholder":3490},[3416,3647,3649],{"class":3418,"line":3648},10,[3416,3650,3651],{"class":3440},"    @temperature.setter\n",[3416,3653,3655,3657,3659,3661,3663,3665,3668,3670,3672,3674,3677],{"class":3418,"line":3654},11,[3416,3656,3437],{"class":3422},[3416,3658,3617],{"class":3440},[3416,3660,3444],{"class":3430},[3416,3662,3448],{"class":3447},[3416,3664,3451],{"class":3430},[3416,3666,3667],{"class":3447},"value",[3416,3669,3457],{"class":3430},[3416,3671,3627],{"class":3426},[3416,3673,3624],{"class":3430},[3416,3675,3676],{"class":3422},"None",[3416,3678,3431],{"class":3430},[3416,3680,3682,3685,3688,3691,3694,3697,3699,3701],{"class":3418,"line":3681},12,[3416,3683,3684],{"class":3634},"        if",[3416,3686,3687],{"class":3422}," not",[3416,3689,3690],{"class":3440}," isinstance",[3416,3692,3693],{"class":3430},"(value, (",[3416,3695,3696],{"class":3426},"int",[3416,3698,3451],{"class":3430},[3416,3700,3627],{"class":3426},[3416,3702,3703],{"class":3430},")):\n",[3416,3705,3707,3710,3713,3715,3718,3721,3724,3727,3730,3733,3736,3739],{"class":3418,"line":3706},13,[3416,3708,3709],{"class":3634},"            raise",[3416,3711,3712],{"class":3426}," TypeError",[3416,3714,3444],{"class":3430},[3416,3716,3717],{"class":3422},"f",[3416,3719,3720],{"class":3500},"\"Температура має бути числом, отримано ",[3416,3722,3723],{"class":3422},"{",[3416,3725,3726],{"class":3426},"type",[3416,3728,3729],{"class":3430},"(value).",[3416,3731,3732],{"class":3447},"__name__",[3416,3734,3735],{"class":3422},"}",[3416,3737,3738],{"class":3500},"\"",[3416,3740,3504],{"class":3430},[3416,3742,3744,3746,3749,3752],{"class":3418,"line":3743},14,[3416,3745,3684],{"class":3634},[3416,3747,3748],{"class":3430}," value \u003C -",[3416,3750,3751],{"class":3483},"273.15",[3416,3753,3431],{"class":3430},[3416,3755,3757,3759,3762,3764,3766,3769,3771,3773,3775,3778],{"class":3418,"line":3756},15,[3416,3758,3709],{"class":3634},[3416,3760,3761],{"class":3426}," ValueError",[3416,3763,3444],{"class":3430},[3416,3765,3717],{"class":3422},[3416,3767,3768],{"class":3500},"\"Температура ",[3416,3770,3723],{"class":3422},[3416,3772,3667],{"class":3430},[3416,3774,3735],{"class":3422},[3416,3776,3777],{"class":3500},"°C нижча за абсолютний нуль\"",[3416,3779,3504],{"class":3430},[3416,3781,3783,3785,3787,3789],{"class":3418,"line":3782},16,[3416,3784,3469],{"class":3422},[3416,3786,3596],{"class":3430},[3416,3788,3627],{"class":3426},[3416,3790,3791],{"class":3430},"(value)\n",[3394,3793,3794,3795,3451,3798,3451,3801,3804,3805,3807],{},"Чудово! Але тепер задача ускладнюється: у вас з'являються класи ",[3398,3796,3797],{},"Thermometer",[3398,3799,3800],{},"Barometer",[3398,3802,3803],{},"Hygrometer"," — і кожен з них має схожі поля з обмеженнями. Копіювати ",[3398,3806,3549],{}," у кожен клас? Це антипатерн.",[3809,3810,3811,3817,3824,3829],"card-group",{},[3812,3813,3816],"card",{"icon":3814,"title":3815},"i-heroicons-document-duplicate","Дублювання @property","Кожен клас з валідованим полем потребує власної пари геттер\u002Fсеттер. При зміні логіки валідації — редагуємо десятки місць.",[3812,3818,3821,3823],{"icon":3819,"title":3820},"i-heroicons-lock-closed","Неможливість переналаштування",[3398,3822,3549],{}," жорстко прив'язаний до одного класу. Не можна легко отримати «поле з валідацією числа в діапазоні» як перевикористовуваний артефакт.",[3812,3825,3828],{"icon":3826,"title":3827},"i-heroicons-chart-bar","Немасштабованість","Якщо у класі 20 числових полів з різними діапазонами — 20 пар геттер\u002Fсеттер. Клас перетворюється на монстра з сотнями рядків шаблонного коду.",[3812,3830,3833],{"icon":3831,"title":3832},"i-heroicons-sparkles","Рішення: дескриптори","Дескриптор дозволяє описати логіку доступу до атрибуту один раз у вигляді окремого класу й перевикористовувати її в будь-якій кількості класів.",[3394,3835,3836,3837,3841],{},"Саме для вирішення цієї проблеми в Python існує ",[3838,3839,3840],"strong",{},"протокол дескрипторів"," — один із найпотужніших і найменш вивчених механізмів мови.",[3843,3844],"hr",{},[3389,3846,3848],{"id":3847},"частина-i-протокол-дескрипторів","Частина I: Протокол дескрипторів",[3850,3851,3853],"h3",{"id":3852},"що-таке-дескриптор","Що таке дескриптор",[3394,3855,3856,3859,3860,3451,3863,3866,3867,3870,3871,3874],{},[3838,3857,3858],{},"Дескриптор"," — це будь-який об'єкт, клас якого визначає хоча б один із спеціальних методів: ",[3398,3861,3862],{},"__get__",[3398,3864,3865],{},"__set__"," або ",[3398,3868,3869],{},"__delete__",". Якщо такий об'єкт присвоїти як ",[3838,3872,3873],{},"атрибут класу"," (не екземпляра!), Python автоматично делегує операції читання, запису і видалення цього атрибуту відповідним методам дескриптора.",[3394,3876,3877],{},"Це фундаментальне визначення варто запам'ятати:",[3879,3880,3881,3882,3884,3885,3451,3887,3866,3889,3891],"important",{},"Дескриптор — це ",[3838,3883,3873],{},", чий тип визначає методи ",[3398,3886,3862],{},[3398,3888,3865],{},[3398,3890,3869],{},". При зверненні до такого атрибуту Python не просто повертає чи зберігає значення — він викликає відповідний метод дескриптора.",[3394,3893,3894],{},"Саме завдяки протоколу дескрипторів у Python реалізовані:",[3896,3897,3898,3904,3910,3916],"ul",{},[3899,3900,3901,3903],"li",{},[3398,3902,3549],{}," — вбудований дескриптор, що об'єднує геттер, сеттер і делетер",[3899,3905,3906,3909],{},[3398,3907,3908],{},"@classmethod"," — дескриптор, що підставляє клас замість екземпляра",[3899,3911,3912,3915],{},[3398,3913,3914],{},"@staticmethod"," — дескриптор, що повертає функцію без жодного прив'язування",[3899,3917,3918,3919],{},"Звичайні методи — функції є non-data дескрипторами, що автоматично підставляють ",[3398,3920,3448],{},[3394,3922,3923,3924,3927],{},"Тобто дескриптори — це не екзотична «темна магія». Це ",[3838,3925,3926],{},"фундамент об'єктної системи Python",", яким ви користуєтесь щодня, навіть не підозрюючи про це.",[3850,3929,3931],{"id":3930},"три-методи-протоколу","Три методи протоколу",[3394,3933,3934],{},"Протокол дескрипторів складається з трьох методів та одного допоміжного:",[3936,3937,3938,3962,3987,3996],"field-group",{},[3939,3940,3943,3944,3947,3948,3950,3951,3954,3955,3957,3958,3961],"field",{"name":3941,"type":3942},"__get__(self, obj, objtype=None)","метод читання","Викликається при ",[3838,3945,3946],{},"читанні"," атрибуту. ",[3398,3949,3448],{}," — сам дескриптор, ",[3398,3952,3953],{},"obj"," — екземпляр, через який відбувається доступ (або ",[3398,3956,3676],{},", якщо доступ через клас), ",[3398,3959,3960],{},"objtype"," — клас екземпляра (або сам клас при доступі через клас).",[3939,3963,3943,3966,3969,3970,3973,3974,3976,3977,3979,3980,3982,3983,3986],{"name":3964,"type":3965},"__set__(self, obj, value)","метод запису",[3838,3967,3968],{},"записі"," значення в атрибут (",[3398,3971,3972],{},"obj.attr = value","). ",[3398,3975,3448],{}," — дескриптор, ",[3398,3978,3953],{}," — екземпляр, ",[3398,3981,3667],{}," — нове значення. Якщо цей метод визначений — дескриптор є ",[3838,3984,3985],{},"data descriptor",".",[3939,3988,3991,3992,3995],{"name":3989,"type":3990},"__delete__(self, obj)","метод видалення","Викликається при виконанні ",[3398,3993,3994],{},"del obj.attr",". Рідко потрібен у практиці, але необхідний для повного контролю над атрибутом.",[3939,3997,4000,4001,4004,4005,4008,4009,4011],{"name":3998,"type":3999},"__set_name__(self, owner, name)","ініціалізація (Python 3.6+)","Спеціальний метод, що викликається ",[3838,4002,4003],{},"одразу при створенні класу"," — ще до будь-яких екземплярів. ",[3398,4006,4007],{},"owner"," — клас, у якому оголошено дескриптор, ",[3398,4010,3454],{}," — ім'я атрибуту, якому присвоєно дескриптор. Усуває необхідність передавати ім'я вручну через конструктор.",[3850,4013,4015],{"id":4014},"перший-дескриптор-покроковий-розбір","Перший дескриптор: покроковий розбір",[3394,4017,4018],{},"Побудуємо найпростіший можливий дескриптор — логер доступу, що фіксує кожне читання і запис атрибуту:",[3407,4020,4022],{"className":3409,"code":4021,"language":3411,"meta":3412,"style":3412},"# descriptors_intro.py\n\nclass LoggedAttribute:\n    \"\"\"\n    Найпростіший дескриптор: логує кожен read та write до атрибуту.\n    Зберігає значення у словнику __dict__ самого екземпляра,\n    використовуючи своє ім'я як ключ.\n    \"\"\"\n\n    def __set_name__(self, owner: type, name: str) -> None:\n        # Викликається при оголошенні класу.\n        # owner = клас, де оголошено дескриптор (наприклад, Person)\n        # name  = ім'я атрибуту класу (наприклад, 'age')\n        self.public_name = name           # 'age'\n        self.private_name = f\"_{name}\"   # '_age' — для зберігання у __dict__ екземпляра\n\n    def __get__(self, obj, objtype=None):\n        # obj = None, якщо доступ через клас: Person.age\n        if obj is None:\n            return self  # повертаємо сам дескриптор\n        value = getattr(obj, self.private_name, None)\n        print(f\"[GET] {type(obj).__name__}.{self.public_name} → {value!r}\")\n        return value\n\n    def __set__(self, obj, value) -> None:\n        print(f\"[SET] {type(obj).__name__}.{self.public_name} = {value!r}\")\n        setattr(obj, self.private_name, value)\n\n\nclass Person:\n    # LoggedAttribute() — це екземпляр класу-дескриптора,\n    # присвоєний як атрибут КЛАСУ (не __init__)\n    name = LoggedAttribute()\n    age  = LoggedAttribute()\n\n    def __init__(self, name: str, age: int) -> None:\n        self.name = name  # → викличе LoggedAttribute.__set__\n        self.age  = age   # → викличе LoggedAttribute.__set__\n\n    def __repr__(self) -> str:\n        return f\"Person({self.name!r}, {self.age})\"  # → викличе __get__ двічі\n\n\np = Person(\"Олена\", 28)\nprint(p)\nprint(f\"\\nРоки через 5 років: {p.age + 5}\")\n\n# Доступ через клас — повертає сам дескриптор\nprint(f\"\\nДескриптор через клас: {Person.age}\")\n",[3398,4023,4024,4029,4033,4042,4047,4052,4057,4062,4066,4070,4103,4108,4113,4118,4128,4151,4155,4182,4188,4204,4215,4236,4284,4292,4297,4323,4366,4379,4384,4389,4399,4405,4411,4417,4423,4428,4462,4473,4483,4488,4506,4539,4544,4549,4565,4574,4606,4611,4617],{"__ignoreMap":3412},[3416,4025,4026],{"class":3418,"line":3419},[3416,4027,4028],{"class":3516},"# descriptors_intro.py\n",[3416,4030,4031],{"class":3418,"line":3434},[3416,4032,3491],{"emptyLinePlaceholder":3490},[3416,4034,4035,4037,4040],{"class":3418,"line":3466},[3416,4036,3423],{"class":3422},[3416,4038,4039],{"class":3426}," LoggedAttribute",[3416,4041,3431],{"class":3430},[3416,4043,4044],{"class":3418,"line":3475},[3416,4045,4046],{"class":3500},"    \"\"\"\n",[3416,4048,4049],{"class":3418,"line":3487},[3416,4050,4051],{"class":3500},"    Найпростіший дескриптор: логує кожен read та write до атрибуту.\n",[3416,4053,4054],{"class":3418,"line":3494},[3416,4055,4056],{"class":3500},"    Зберігає значення у словнику __dict__ самого екземпляра,\n",[3416,4058,4059],{"class":3418,"line":3507},[3416,4060,4061],{"class":3500},"    використовуючи своє ім'я як ключ.\n",[3416,4063,4064],{"class":3418,"line":3520},[3416,4065,4046],{"class":3500},[3416,4067,4068],{"class":3418,"line":3532},[3416,4069,3491],{"emptyLinePlaceholder":3490},[3416,4071,4072,4074,4077,4079,4081,4083,4085,4087,4089,4091,4093,4095,4097,4099,4101],{"class":3418,"line":3648},[3416,4073,3437],{"class":3422},[3416,4075,4076],{"class":3440}," __set_name__",[3416,4078,3444],{"class":3430},[3416,4080,3448],{"class":3447},[3416,4082,3451],{"class":3430},[3416,4084,4007],{"class":3447},[3416,4086,3457],{"class":3430},[3416,4088,3726],{"class":3426},[3416,4090,3451],{"class":3430},[3416,4092,3454],{"class":3447},[3416,4094,3457],{"class":3430},[3416,4096,3460],{"class":3426},[3416,4098,3624],{"class":3430},[3416,4100,3676],{"class":3422},[3416,4102,3431],{"class":3430},[3416,4104,4105],{"class":3418,"line":3654},[3416,4106,4107],{"class":3516},"        # Викликається при оголошенні класу.\n",[3416,4109,4110],{"class":3418,"line":3681},[3416,4111,4112],{"class":3516},"        # owner = клас, де оголошено дескриптор (наприклад, Person)\n",[3416,4114,4115],{"class":3418,"line":3706},[3416,4116,4117],{"class":3516},"        # name  = ім'я атрибуту класу (наприклад, 'age')\n",[3416,4119,4120,4122,4125],{"class":3418,"line":3743},[3416,4121,3469],{"class":3422},[3416,4123,4124],{"class":3430},".public_name = name           ",[3416,4126,4127],{"class":3516},"# 'age'\n",[3416,4129,4130,4132,4135,4137,4140,4142,4144,4146,4148],{"class":3418,"line":3756},[3416,4131,3469],{"class":3422},[3416,4133,4134],{"class":3430},".private_name = ",[3416,4136,3717],{"class":3422},[3416,4138,4139],{"class":3500},"\"_",[3416,4141,3723],{"class":3422},[3416,4143,3454],{"class":3430},[3416,4145,3735],{"class":3422},[3416,4147,3738],{"class":3500},[3416,4149,4150],{"class":3516},"   # '_age' — для зберігання у __dict__ екземпляра\n",[3416,4152,4153],{"class":3418,"line":3782},[3416,4154,3491],{"emptyLinePlaceholder":3490},[3416,4156,4158,4160,4163,4165,4167,4169,4171,4173,4175,4178,4180],{"class":3418,"line":4157},17,[3416,4159,3437],{"class":3422},[3416,4161,4162],{"class":3440}," __get__",[3416,4164,3444],{"class":3430},[3416,4166,3448],{"class":3447},[3416,4168,3451],{"class":3430},[3416,4170,3953],{"class":3447},[3416,4172,3451],{"class":3430},[3416,4174,3960],{"class":3447},[3416,4176,4177],{"class":3430},"=",[3416,4179,3676],{"class":3422},[3416,4181,3463],{"class":3430},[3416,4183,4185],{"class":3418,"line":4184},18,[3416,4186,4187],{"class":3516},"        # obj = None, якщо доступ через клас: Person.age\n",[3416,4189,4191,4193,4196,4199,4202],{"class":3418,"line":4190},19,[3416,4192,3684],{"class":3634},[3416,4194,4195],{"class":3430}," obj ",[3416,4197,4198],{"class":3422},"is",[3416,4200,4201],{"class":3422}," None",[3416,4203,3431],{"class":3430},[3416,4205,4207,4210,4212],{"class":3418,"line":4206},20,[3416,4208,4209],{"class":3634},"            return",[3416,4211,3638],{"class":3422},[3416,4213,4214],{"class":3516},"  # повертаємо сам дескриптор\n",[3416,4216,4218,4221,4224,4227,4229,4232,4234],{"class":3418,"line":4217},21,[3416,4219,4220],{"class":3430},"        value = ",[3416,4222,4223],{"class":3440},"getattr",[3416,4225,4226],{"class":3430},"(obj, ",[3416,4228,3448],{"class":3422},[3416,4230,4231],{"class":3430},".private_name, ",[3416,4233,3676],{"class":3422},[3416,4235,3504],{"class":3430},[3416,4237,4239,4242,4244,4246,4249,4251,4253,4256,4258,4260,4262,4265,4268,4270,4273,4275,4277,4280,4282],{"class":3418,"line":4238},22,[3416,4240,4241],{"class":3440},"        print",[3416,4243,3444],{"class":3430},[3416,4245,3717],{"class":3422},[3416,4247,4248],{"class":3500},"\"[GET] ",[3416,4250,3723],{"class":3422},[3416,4252,3726],{"class":3426},[3416,4254,4255],{"class":3430},"(obj).",[3416,4257,3732],{"class":3447},[3416,4259,3735],{"class":3422},[3416,4261,3986],{"class":3500},[3416,4263,4264],{"class":3422},"{self",[3416,4266,4267],{"class":3430},".public_name",[3416,4269,3735],{"class":3422},[3416,4271,4272],{"class":3500}," → ",[3416,4274,3723],{"class":3422},[3416,4276,3667],{"class":3430},[3416,4278,4279],{"class":3422},"!r}",[3416,4281,3738],{"class":3500},[3416,4283,3504],{"class":3430},[3416,4285,4287,4289],{"class":3418,"line":4286},23,[3416,4288,3635],{"class":3634},[3416,4290,4291],{"class":3430}," value\n",[3416,4293,4295],{"class":3418,"line":4294},24,[3416,4296,3491],{"emptyLinePlaceholder":3490},[3416,4298,4300,4302,4305,4307,4309,4311,4313,4315,4317,4319,4321],{"class":3418,"line":4299},25,[3416,4301,3437],{"class":3422},[3416,4303,4304],{"class":3440}," __set__",[3416,4306,3444],{"class":3430},[3416,4308,3448],{"class":3447},[3416,4310,3451],{"class":3430},[3416,4312,3953],{"class":3447},[3416,4314,3451],{"class":3430},[3416,4316,3667],{"class":3447},[3416,4318,3624],{"class":3430},[3416,4320,3676],{"class":3422},[3416,4322,3431],{"class":3430},[3416,4324,4326,4328,4330,4332,4335,4337,4339,4341,4343,4345,4347,4349,4351,4353,4356,4358,4360,4362,4364],{"class":3418,"line":4325},26,[3416,4327,4241],{"class":3440},[3416,4329,3444],{"class":3430},[3416,4331,3717],{"class":3422},[3416,4333,4334],{"class":3500},"\"[SET] ",[3416,4336,3723],{"class":3422},[3416,4338,3726],{"class":3426},[3416,4340,4255],{"class":3430},[3416,4342,3732],{"class":3447},[3416,4344,3735],{"class":3422},[3416,4346,3986],{"class":3500},[3416,4348,4264],{"class":3422},[3416,4350,4267],{"class":3430},[3416,4352,3735],{"class":3422},[3416,4354,4355],{"class":3500}," = ",[3416,4357,3723],{"class":3422},[3416,4359,3667],{"class":3430},[3416,4361,4279],{"class":3422},[3416,4363,3738],{"class":3500},[3416,4365,3504],{"class":3430},[3416,4367,4369,4372,4374,4376],{"class":3418,"line":4368},27,[3416,4370,4371],{"class":3440},"        setattr",[3416,4373,4226],{"class":3430},[3416,4375,3448],{"class":3422},[3416,4377,4378],{"class":3430},".private_name, value)\n",[3416,4380,4382],{"class":3418,"line":4381},28,[3416,4383,3491],{"emptyLinePlaceholder":3490},[3416,4385,4387],{"class":3418,"line":4386},29,[3416,4388,3491],{"emptyLinePlaceholder":3490},[3416,4390,4392,4394,4397],{"class":3418,"line":4391},30,[3416,4393,3423],{"class":3422},[3416,4395,4396],{"class":3426}," Person",[3416,4398,3431],{"class":3430},[3416,4400,4402],{"class":3418,"line":4401},31,[3416,4403,4404],{"class":3516},"    # LoggedAttribute() — це екземпляр класу-дескриптора,\n",[3416,4406,4408],{"class":3418,"line":4407},32,[3416,4409,4410],{"class":3516},"    # присвоєний як атрибут КЛАСУ (не __init__)\n",[3416,4412,4414],{"class":3418,"line":4413},33,[3416,4415,4416],{"class":3430},"    name = LoggedAttribute()\n",[3416,4418,4420],{"class":3418,"line":4419},34,[3416,4421,4422],{"class":3430},"    age  = LoggedAttribute()\n",[3416,4424,4426],{"class":3418,"line":4425},35,[3416,4427,3491],{"emptyLinePlaceholder":3490},[3416,4429,4431,4433,4435,4437,4439,4441,4443,4445,4447,4449,4452,4454,4456,4458,4460],{"class":3418,"line":4430},36,[3416,4432,3437],{"class":3422},[3416,4434,3441],{"class":3440},[3416,4436,3444],{"class":3430},[3416,4438,3448],{"class":3447},[3416,4440,3451],{"class":3430},[3416,4442,3454],{"class":3447},[3416,4444,3457],{"class":3430},[3416,4446,3460],{"class":3426},[3416,4448,3451],{"class":3430},[3416,4450,4451],{"class":3447},"age",[3416,4453,3457],{"class":3430},[3416,4455,3696],{"class":3426},[3416,4457,3624],{"class":3430},[3416,4459,3676],{"class":3422},[3416,4461,3431],{"class":3430},[3416,4463,4465,4467,4470],{"class":3418,"line":4464},37,[3416,4466,3469],{"class":3422},[3416,4468,4469],{"class":3430},".name = name  ",[3416,4471,4472],{"class":3516},"# → викличе LoggedAttribute.__set__\n",[3416,4474,4476,4478,4481],{"class":3418,"line":4475},38,[3416,4477,3469],{"class":3422},[3416,4479,4480],{"class":3430},".age  = age   ",[3416,4482,4472],{"class":3516},[3416,4484,4486],{"class":3418,"line":4485},39,[3416,4487,3491],{"emptyLinePlaceholder":3490},[3416,4489,4491,4493,4496,4498,4500,4502,4504],{"class":3418,"line":4490},40,[3416,4492,3437],{"class":3422},[3416,4494,4495],{"class":3440}," __repr__",[3416,4497,3444],{"class":3430},[3416,4499,3448],{"class":3447},[3416,4501,3624],{"class":3430},[3416,4503,3460],{"class":3426},[3416,4505,3431],{"class":3430},[3416,4507,4509,4511,4514,4517,4519,4522,4524,4526,4528,4531,4533,4536],{"class":3418,"line":4508},41,[3416,4510,3635],{"class":3634},[3416,4512,4513],{"class":3422}," f",[3416,4515,4516],{"class":3500},"\"Person(",[3416,4518,4264],{"class":3422},[3416,4520,4521],{"class":3430},".name",[3416,4523,4279],{"class":3422},[3416,4525,3451],{"class":3500},[3416,4527,4264],{"class":3422},[3416,4529,4530],{"class":3430},".age",[3416,4532,3735],{"class":3422},[3416,4534,4535],{"class":3500},")\"",[3416,4537,4538],{"class":3516},"  # → викличе __get__ двічі\n",[3416,4540,4542],{"class":3418,"line":4541},42,[3416,4543,3491],{"emptyLinePlaceholder":3490},[3416,4545,4547],{"class":3418,"line":4546},43,[3416,4548,3491],{"emptyLinePlaceholder":3490},[3416,4550,4552,4555,4558,4560,4563],{"class":3418,"line":4551},44,[3416,4553,4554],{"class":3430},"p = Person(",[3416,4556,4557],{"class":3500},"\"Олена\"",[3416,4559,3451],{"class":3430},[3416,4561,4562],{"class":3483},"28",[3416,4564,3504],{"class":3430},[3416,4566,4568,4571],{"class":3418,"line":4567},45,[3416,4569,4570],{"class":3440},"print",[3416,4572,4573],{"class":3430},"(p)\n",[3416,4575,4577,4579,4581,4583,4585,4589,4592,4594,4597,4600,4602,4604],{"class":3418,"line":4576},46,[3416,4578,4570],{"class":3440},[3416,4580,3444],{"class":3430},[3416,4582,3717],{"class":3422},[3416,4584,3738],{"class":3500},[3416,4586,4588],{"class":4587},"sjcCO","\\n",[3416,4590,4591],{"class":3500},"Роки через 5 років: ",[3416,4593,3723],{"class":3422},[3416,4595,4596],{"class":3430},"p.age + ",[3416,4598,4599],{"class":3483},"5",[3416,4601,3735],{"class":3422},[3416,4603,3738],{"class":3500},[3416,4605,3504],{"class":3430},[3416,4607,4609],{"class":3418,"line":4608},47,[3416,4610,3491],{"emptyLinePlaceholder":3490},[3416,4612,4614],{"class":3418,"line":4613},48,[3416,4615,4616],{"class":3516},"# Доступ через клас — повертає сам дескриптор\n",[3416,4618,4620,4622,4624,4626,4628,4630,4633,4635,4638,4640,4642],{"class":3418,"line":4619},49,[3416,4621,4570],{"class":3440},[3416,4623,3444],{"class":3430},[3416,4625,3717],{"class":3422},[3416,4627,3738],{"class":3500},[3416,4629,4588],{"class":4587},[3416,4631,4632],{"class":3500},"Дескриптор через клас: ",[3416,4634,3723],{"class":3422},[3416,4636,4637],{"class":3430},"Person.age",[3416,4639,3735],{"class":3422},[3416,4641,3738],{"class":3500},[3416,4643,3504],{"class":3430},[4645,4646,4648,4660,4669,4676,4684,4691,4695,4698,4704,4712,4715],"terminal-preview",{"title":4647},"python descriptors_intro.py",[4649,4650,4652,4657,4658],"div",{"className":4651},[3418],[3416,4653,4656],{"className":4654},[4655],"opacity-40","$"," ",[3838,4659,4647],{},[4649,4661,4663,4664],{"className":4662},[3418],"[SET] Person.name = ",[3416,4665,4668],{"className":4666},[4667],"text-blue-400","'Олена'",[4649,4670,4672,4673],{"className":4671},[3418],"[SET] Person.age = ",[3416,4674,4562],{"className":4675},[4667],[4649,4677,4679,4680],{"className":4678},[3418],"[GET] Person.name → ",[3416,4681,4668],{"className":4682},[4683],"text-green-400",[4649,4685,4687,4688],{"className":4686},[3418],"[GET] Person.age → ",[3416,4689,4562],{"className":4690},[4683],[4649,4692,4694],{"className":4693},[3418],"Person('Олена', 28)",[4649,4696],{"className":4697},[3418],[4649,4699,4687,4701],{"className":4700},[3418],[3416,4702,4562],{"className":4703},[4683],[4649,4705,4591,4707],{"className":4706},[3418],[3416,4708,4711],{"className":4709},[4710],"text-yellow-400","33",[4649,4713],{"className":4714},[3418],[4649,4716,4632,4718],{"className":4717},[3418],[3416,4719,4722],{"className":4720},[4721],"text-gray-400","\u003CLoggedAttribute object at 0x...>",[3394,4724,4725],{},"Розберемо ключові моменти цього прикладу:",[3394,4727,4728],{},[3838,4729,4730,4731,4734,4735,4738],{},"Чому ",[3398,4732,4733],{},"setattr(obj, self.private_name, value)",", а не ",[3398,4736,4737],{},"obj.__dict__[self.public_name] = value","?",[3394,4740,4741,4742,4745,4746,4272,4749,4272,4752,4755,4756,4759,4760,4763],{},"Якби ми зберігали значення за тим самим ім'ям, що й дескриптор (",[3398,4743,4744],{},"self.public_name = 'age'","), виник би нескінченний рекурсивний виклик: ",[3398,4747,4748],{},"self.age = 28",[3398,4750,4751],{},"LoggedAttribute.__set__",[3398,4753,4754],{},"obj.__dict__['age'] = 28"," — але запис у ",[3398,4757,4758],{},"__dict__"," за іменем, яким є дескриптор, знову перехоплюється дескриптором. Тому ми зберігаємо значення під «приватним» ім'ям ",[3398,4761,4762],{},"_age",", яке не є дескриптором.",[3394,4765,4766],{},[3838,4767,4730,4768,4771,4772,4738],{},[3398,4769,4770],{},"obj is None"," у ",[3398,4773,3862],{},[3394,4775,4776,4777,4780,4781,4783,4784,4787,4788,4791],{},"Коли ви звертаєтесь до атрибуту через ",[3838,4778,4779],{},"клас"," (",[3398,4782,4637],{},"), Python викликає ",[3398,4785,4786],{},"__get__(None, Person)",". Конвенційна відповідь — повернути сам об'єкт дескриптора (",[3398,4789,4790],{},"return self","). Це дозволяє отримати доступ до дескриптора для інтроспекції.",[3394,4793,4794],{},[3838,4795,4796,4797,3550],{},"Роль ",[3398,4798,4799],{},"__set_name__",[3394,4801,4802,4803,4806,4807,4809],{},"До Python 3.6 дескриптор не знав свого імені у класі — його треба було передавати вручну через конструктор: ",[3398,4804,4805],{},"age = LoggedAttribute('age')",". Метод ",[3398,4808,4799],{}," (PEP 487) усунув цю незручність: Python сам викликає його одразу після оголошення класу, передаючи ім'я атрибуту.",[3843,4811],{},[3389,4813,4815],{"id":4814},"частина-ii-алгоритм-пошуку-атрибутів-attribute-lookup-chain","Частина II: Алгоритм пошуку атрибутів (Attribute Lookup Chain)",[3850,4817,4819,4820,4823],{"id":4818},"як-python-вирішує-objattr-повна-картина","Як Python вирішує ",[3398,4821,4822],{},"obj.attr",": повна картина",[3394,4825,4826,4827,4829,4830,3451,4832,4834],{},"Коли ви пишете ",[3398,4828,4822],{},", Python не просто «шукає атрибут». Він виконує складний алгоритм із чітко визначеним пріоритетом. Розуміння цього алгоритму — ключ до того, щоб передбачити поведінку дескрипторів, ",[3398,4831,3549],{},[3398,4833,4758],{}," та звичайних атрибутів у будь-якій ситуації.",[3394,4836,4837,4838,4841],{},"Алгоритм реалізований у вбудованому методі ",[3398,4839,4840],{},"object.__getattribute__",". Саме він викликається кожного разу, коли ви звертаєтесь до атрибуту через крапку. Спрощено його логіку можна описати так:",[4843,4844,4845,4849,4882,4889,4907,4911,4933,4941],"steps",{},[3850,4846,4848],{"id":4847},"крок-1-пошук-у-типі-класі-та-його-mro","Крок 1: Пошук у типі (класі) та його MRO",[3394,4850,4851,4852,4771,4855,4858,4859,4861,4862,4657,4864,4657,4867,3866,4869,4871,4872,4875,4876,4878,4879,4881],{},"Python шукає ",[3398,4853,4854],{},"attr",[3398,4856,4857],{},"type(obj).__mro__"," — ланцюжку успадкування. Якщо знаходить ",[3838,4860,3985],{}," (клас атрибуту визначає ",[3398,4863,3862],{},[3838,4865,4866],{},"і",[3398,4868,3865],{},[3398,4870,3869],{},"), то ",[3838,4873,4874],{},"негайно"," викликає його ",[3398,4877,3862],{},". Data descriptor має найвищий пріоритет — він перемагає навіть ",[3398,4880,4758],{}," екземпляра.",[3850,4883,4885,4886,4888],{"id":4884},"крок-2-пошук-у-__dict__-екземпляра","Крок 2: Пошук у ",[3398,4887,4758],{}," екземпляра",[3394,4890,4891,4892,4895,4896,4898,4899,4902,4903,4906],{},"Якщо на кроці 1 data descriptor не знайдено, Python перевіряє ",[3398,4893,4894],{},"obj.__dict__",". Якщо ключ ",[3398,4897,4854],{}," там є — повертає відповідне значення. Звичайні атрибути, встановлені через ",[3398,4900,4901],{},"self.x = ..."," в ",[3398,4904,4905],{},"__init__",", живуть саме тут.",[3850,4908,4910],{"id":4909},"крок-3-non-data-descriptor-або-атрибут-класу","Крок 3: Non-data descriptor або атрибут класу",[3394,4912,4913,4914,4916,4917,4920,4921,4923,4924,4926,4927,4929,4930,4932],{},"Якщо ключ не знайдено у ",[3398,4915,4758],{}," екземпляра, Python повертається до того, що знайшов у типі на кроці 1. Якщо це ",[3838,4918,4919],{},"non-data descriptor"," (є ",[3398,4922,3862],{},", але немає ",[3398,4925,3865],{},"\u002F",[3398,4928,3869],{},") — викликає його ",[3398,4931,3862],{},". Якщо це просто атрибут класу (не дескриптор) — повертає його значення напряму.",[3850,4934,4936,4937,4940],{"id":4935},"крок-4-__getattr__-як-запасний-варіант","Крок 4: ",[3398,4938,4939],{},"__getattr__"," як запасний варіант",[3394,4942,4943,4944,4946,4947,3986],{},"Якщо атрибут не знайдено жодним із попередніх кроків, Python шукає метод ",[3398,4945,4939],{}," у класі. Якщо він є — викликає його. Якщо ні — підіймає ",[3398,4948,4949],{},"AttributeError",[4951,4952,4953],"plant-uml",{},[3407,4954,4958],{"className":4955,"code":4956,"language":4957,"meta":3412,"style":3412},"language-plantuml shiki shiki-themes light-plus dark-plus dark-plus","@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam ArrowColor #6366f1\nskinparam ActivityBorderColor #6366f1\nskinparam ActivityBackgroundColor #f8fafc\n\nstart\n\n:obj.attr;\n\n:Шукати attr у type(obj).__mro__;\n\nif (Знайдено data descriptor?\\n(__get__ + __set__ або __delete__)) then (Так)\n  #d1fae5:Викликати descriptor.__get__(obj, type(obj));\n  :Повернути результат;\n  stop\nelse (Ні)\nendif\n\nif (attr є у obj.__dict__?) then (Так)\n  #dbeafe:Повернути obj.__dict__['attr'];\n  stop\nelse (Ні)\nendif\n\nif (Знайдено non-data descriptor?\\n(тільки __get__, без __set__)) then (Так)\n  #fef3c7:Викликати descriptor.__get__(obj, type(obj));\n  :Повернути результат;\n  stop\nelse (Ні)\nendif\n\nif (Знайдено звичайний атрибут класу?) then (Так)\n  #fee2e2:Повернути значення атрибуту класу;\n  stop\nelse (Ні)\nendif\n\nif (__getattr__ визначено у класі?) then (Так)\n  :Викликати __getattr__(obj, 'attr');\n  stop\nelse (Ні)\n  #f87171:Підняти AttributeError;\n  stop\nendif\n\n@enduml\n","plantuml",[3398,4959,4960,4965,4970,4975,4980,4985,4990,4994,4999,5003,5008,5012,5017,5021,5026,5031,5036,5041,5046,5051,5055,5060,5065,5069,5073,5077,5081,5086,5091,5095,5099,5103,5107,5111,5116,5121,5125,5129,5133,5137,5142,5147,5151,5155,5160,5164,5168,5172],{"__ignoreMap":3412},[3416,4961,4962],{"class":3418,"line":3419},[3416,4963,4964],{},"@startuml\n",[3416,4966,4967],{"class":3418,"line":3434},[3416,4968,4969],{},"skinparam style plain\n",[3416,4971,4972],{"class":3418,"line":3466},[3416,4973,4974],{},"skinparam backgroundColor #ffffff\n",[3416,4976,4977],{"class":3418,"line":3475},[3416,4978,4979],{},"skinparam ArrowColor #6366f1\n",[3416,4981,4982],{"class":3418,"line":3487},[3416,4983,4984],{},"skinparam ActivityBorderColor #6366f1\n",[3416,4986,4987],{"class":3418,"line":3494},[3416,4988,4989],{},"skinparam ActivityBackgroundColor #f8fafc\n",[3416,4991,4992],{"class":3418,"line":3507},[3416,4993,3491],{"emptyLinePlaceholder":3490},[3416,4995,4996],{"class":3418,"line":3520},[3416,4997,4998],{},"start\n",[3416,5000,5001],{"class":3418,"line":3532},[3416,5002,3491],{"emptyLinePlaceholder":3490},[3416,5004,5005],{"class":3418,"line":3648},[3416,5006,5007],{},":obj.attr;\n",[3416,5009,5010],{"class":3418,"line":3654},[3416,5011,3491],{"emptyLinePlaceholder":3490},[3416,5013,5014],{"class":3418,"line":3681},[3416,5015,5016],{},":Шукати attr у type(obj).__mro__;\n",[3416,5018,5019],{"class":3418,"line":3706},[3416,5020,3491],{"emptyLinePlaceholder":3490},[3416,5022,5023],{"class":3418,"line":3743},[3416,5024,5025],{},"if (Знайдено data descriptor?\\n(__get__ + __set__ або __delete__)) then (Так)\n",[3416,5027,5028],{"class":3418,"line":3756},[3416,5029,5030],{},"  #d1fae5:Викликати descriptor.__get__(obj, type(obj));\n",[3416,5032,5033],{"class":3418,"line":3782},[3416,5034,5035],{},"  :Повернути результат;\n",[3416,5037,5038],{"class":3418,"line":4157},[3416,5039,5040],{},"  stop\n",[3416,5042,5043],{"class":3418,"line":4184},[3416,5044,5045],{},"else (Ні)\n",[3416,5047,5048],{"class":3418,"line":4190},[3416,5049,5050],{},"endif\n",[3416,5052,5053],{"class":3418,"line":4206},[3416,5054,3491],{"emptyLinePlaceholder":3490},[3416,5056,5057],{"class":3418,"line":4217},[3416,5058,5059],{},"if (attr є у obj.__dict__?) then (Так)\n",[3416,5061,5062],{"class":3418,"line":4238},[3416,5063,5064],{},"  #dbeafe:Повернути obj.__dict__['attr'];\n",[3416,5066,5067],{"class":3418,"line":4286},[3416,5068,5040],{},[3416,5070,5071],{"class":3418,"line":4294},[3416,5072,5045],{},[3416,5074,5075],{"class":3418,"line":4299},[3416,5076,5050],{},[3416,5078,5079],{"class":3418,"line":4325},[3416,5080,3491],{"emptyLinePlaceholder":3490},[3416,5082,5083],{"class":3418,"line":4368},[3416,5084,5085],{},"if (Знайдено non-data descriptor?\\n(тільки __get__, без __set__)) then (Так)\n",[3416,5087,5088],{"class":3418,"line":4381},[3416,5089,5090],{},"  #fef3c7:Викликати descriptor.__get__(obj, type(obj));\n",[3416,5092,5093],{"class":3418,"line":4386},[3416,5094,5035],{},[3416,5096,5097],{"class":3418,"line":4391},[3416,5098,5040],{},[3416,5100,5101],{"class":3418,"line":4401},[3416,5102,5045],{},[3416,5104,5105],{"class":3418,"line":4407},[3416,5106,5050],{},[3416,5108,5109],{"class":3418,"line":4413},[3416,5110,3491],{"emptyLinePlaceholder":3490},[3416,5112,5113],{"class":3418,"line":4419},[3416,5114,5115],{},"if (Знайдено звичайний атрибут класу?) then (Так)\n",[3416,5117,5118],{"class":3418,"line":4425},[3416,5119,5120],{},"  #fee2e2:Повернути значення атрибуту класу;\n",[3416,5122,5123],{"class":3418,"line":4430},[3416,5124,5040],{},[3416,5126,5127],{"class":3418,"line":4464},[3416,5128,5045],{},[3416,5130,5131],{"class":3418,"line":4475},[3416,5132,5050],{},[3416,5134,5135],{"class":3418,"line":4485},[3416,5136,3491],{"emptyLinePlaceholder":3490},[3416,5138,5139],{"class":3418,"line":4490},[3416,5140,5141],{},"if (__getattr__ визначено у класі?) then (Так)\n",[3416,5143,5144],{"class":3418,"line":4508},[3416,5145,5146],{},"  :Викликати __getattr__(obj, 'attr');\n",[3416,5148,5149],{"class":3418,"line":4541},[3416,5150,5040],{},[3416,5152,5153],{"class":3418,"line":4546},[3416,5154,5045],{},[3416,5156,5157],{"class":3418,"line":4551},[3416,5158,5159],{},"  #f87171:Підняти AttributeError;\n",[3416,5161,5162],{"class":3418,"line":4567},[3416,5163,5040],{},[3416,5165,5166],{"class":3418,"line":4576},[3416,5167,5050],{},[3416,5169,5170],{"class":3418,"line":4608},[3416,5171,3491],{"emptyLinePlaceholder":3490},[3416,5173,5174],{"class":3418,"line":4613},[3416,5175,5176],{},"@enduml\n",[3850,5178,5180],{"id":5179},"data-descriptor-vs-non-data-descriptor-критична-різниця","Data descriptor vs Non-data descriptor: критична різниця",[3394,5182,5183],{},"Найважливіше розмежування у протоколі дескрипторів — це поділ на два типи:",[5185,5186,5187,5203],"table",{},[5188,5189,5190],"thead",{},[5191,5192,5193,5197,5200],"tr",{},[5194,5195,5196],"th",{},"Характеристика",[5194,5198,5199],{},"Data Descriptor",[5194,5201,5202],{},"Non-data Descriptor",[5204,5205,5206,5227,5247,5262],"tbody",{},[5191,5207,5208,5212,5222],{},[5209,5210,5211],"td",{},"Визначені методи",[5209,5213,5214,5216,5217,3866,5219,5221],{},[3398,5215,3862],{}," + (",[3398,5218,3865],{},[3398,5220,3869],{},")",[5209,5223,5224,5225],{},"лише ",[3398,5226,3862],{},[5191,5228,5229,5232,5240],{},[5209,5230,5231],{},"Пріоритет",[5209,5233,5234,5237,5238,4888],{},[3838,5235,5236],{},"Вищий"," за ",[3398,5239,4758],{},[5209,5241,5242,5237,5245,4888],{},[3838,5243,5244],{},"Нижчий",[3398,5246,4758],{},[5191,5248,5249,5252,5259],{},[5209,5250,5251],{},"Типові приклади",[5209,5253,5254,3451,5256],{},[3398,5255,3549],{},[3398,5257,5258],{},"IntegerField",[5209,5260,5261],{},"Звичайні функції\u002Fметоди",[5191,5263,5264,5269,5275],{},[5209,5265,5266,5267],{},"Поведінка при ",[3398,5268,3972],{},[5209,5270,5271,5272,5274],{},"Викликає ",[3398,5273,3865],{}," дескриптора",[5209,5276,5277,5278,3451,5280,5283],{},"Записує у ",[3398,5279,4894],{},[3838,5281,5282],{},"затіняючи"," дескриптор",[3394,5285,5286],{},"Ця різниця має принципове значення. Розберемо її на прикладі:",[3407,5288,5290],{"className":3409,"code":5289,"language":3411,"meta":3412,"style":3412},"# data_vs_nondata.py\n\nclass DataDescriptor:\n    \"\"\"Data descriptor — має і __get__, і __set__.\"\"\"\n\n    def __get__(self, obj, objtype=None):\n        if obj is None:\n            return self\n        return obj.__dict__.get(f\"_dd_val\", \"значення не встановлено\")\n\n    def __set__(self, obj, value):\n        print(f\"  [DataDescriptor.__set__] value={value!r}\")\n        obj.__dict__[\"_dd_val\"] = value\n\n\nclass NonDataDescriptor:\n    \"\"\"Non-data descriptor — лише __get__, без __set__.\"\"\"\n\n    def __get__(self, obj, objtype=None):\n        if obj is None:\n            return self\n        print(f\"  [NonDataDescriptor.__get__] повертаю 'дескриптор'\")\n        return \"значення від дескриптора\"\n\n\nclass MyClass:\n    data_attr     = DataDescriptor()\n    nondata_attr  = NonDataDescriptor()\n\n\nobj = MyClass()\n\n# === Experiment 1: Data Descriptor ===\nprint(\"=== Data Descriptor ===\")\nprint(f\"До запису: {obj.data_attr}\")      # виклик __get__, значення відсутнє\n\nobj.data_attr = 42                         # → DataDescriptor.__set__\nprint(f\"Після запису: {obj.data_attr}\")   # → DataDescriptor.__get__\n\n# Спроба \"обійти\" дескриптор — записати у __dict__ напряму\nobj.__dict__[\"data_attr\"] = 9999\nprint(f\"Після __dict__['data_attr']=9999: {obj.data_attr}\")\n# Data descriptor ігнорує __dict__ і все одно викликає __get__!\n\n# === Experiment 2: Non-data Descriptor ===\nprint(\"\\n=== Non-data Descriptor ===\")\nprint(f\"До запису: {obj.nondata_attr}\")   # → NonDataDescriptor.__get__\n\n# Запис через звичайне присвоєння — обходить дескриптор!\nobj.nondata_attr = \"моє власне значення\"\nprint(f\"Після запису: {obj.nondata_attr}\") # тепер читаємо з __dict__ екземпляра!\n\nprint(f\"obj.__dict__: {obj.__dict__}\")\n",[3398,5291,5292,5297,5301,5310,5315,5319,5343,5355,5362,5386,5390,5410,5431,5446,5450,5454,5463,5468,5472,5496,5508,5514,5527,5534,5538,5542,5551,5556,5561,5565,5569,5574,5578,5583,5594,5620,5624,5635,5660,5664,5669,5687,5708,5713,5717,5722,5737,5761,5765,5770,5779,5804,5809],{"__ignoreMap":3412},[3416,5293,5294],{"class":3418,"line":3419},[3416,5295,5296],{"class":3516},"# data_vs_nondata.py\n",[3416,5298,5299],{"class":3418,"line":3434},[3416,5300,3491],{"emptyLinePlaceholder":3490},[3416,5302,5303,5305,5308],{"class":3418,"line":3466},[3416,5304,3423],{"class":3422},[3416,5306,5307],{"class":3426}," DataDescriptor",[3416,5309,3431],{"class":3430},[3416,5311,5312],{"class":3418,"line":3475},[3416,5313,5314],{"class":3500},"    \"\"\"Data descriptor — має і __get__, і __set__.\"\"\"\n",[3416,5316,5317],{"class":3418,"line":3487},[3416,5318,3491],{"emptyLinePlaceholder":3490},[3416,5320,5321,5323,5325,5327,5329,5331,5333,5335,5337,5339,5341],{"class":3418,"line":3494},[3416,5322,3437],{"class":3422},[3416,5324,4162],{"class":3440},[3416,5326,3444],{"class":3430},[3416,5328,3448],{"class":3447},[3416,5330,3451],{"class":3430},[3416,5332,3953],{"class":3447},[3416,5334,3451],{"class":3430},[3416,5336,3960],{"class":3447},[3416,5338,4177],{"class":3430},[3416,5340,3676],{"class":3422},[3416,5342,3463],{"class":3430},[3416,5344,5345,5347,5349,5351,5353],{"class":3418,"line":3507},[3416,5346,3684],{"class":3634},[3416,5348,4195],{"class":3430},[3416,5350,4198],{"class":3422},[3416,5352,4201],{"class":3422},[3416,5354,3431],{"class":3430},[3416,5356,5357,5359],{"class":3418,"line":3520},[3416,5358,4209],{"class":3634},[3416,5360,5361],{"class":3422}," self\n",[3416,5363,5364,5366,5369,5371,5374,5376,5379,5381,5384],{"class":3418,"line":3532},[3416,5365,3635],{"class":3634},[3416,5367,5368],{"class":3430}," obj.",[3416,5370,4758],{"class":3447},[3416,5372,5373],{"class":3430},".get(",[3416,5375,3717],{"class":3422},[3416,5377,5378],{"class":3500},"\"_dd_val\"",[3416,5380,3451],{"class":3430},[3416,5382,5383],{"class":3500},"\"значення не встановлено\"",[3416,5385,3504],{"class":3430},[3416,5387,5388],{"class":3418,"line":3648},[3416,5389,3491],{"emptyLinePlaceholder":3490},[3416,5391,5392,5394,5396,5398,5400,5402,5404,5406,5408],{"class":3418,"line":3654},[3416,5393,3437],{"class":3422},[3416,5395,4304],{"class":3440},[3416,5397,3444],{"class":3430},[3416,5399,3448],{"class":3447},[3416,5401,3451],{"class":3430},[3416,5403,3953],{"class":3447},[3416,5405,3451],{"class":3430},[3416,5407,3667],{"class":3447},[3416,5409,3463],{"class":3430},[3416,5411,5412,5414,5416,5418,5421,5423,5425,5427,5429],{"class":3418,"line":3681},[3416,5413,4241],{"class":3440},[3416,5415,3444],{"class":3430},[3416,5417,3717],{"class":3422},[3416,5419,5420],{"class":3500},"\"  [DataDescriptor.__set__] value=",[3416,5422,3723],{"class":3422},[3416,5424,3667],{"class":3430},[3416,5426,4279],{"class":3422},[3416,5428,3738],{"class":3500},[3416,5430,3504],{"class":3430},[3416,5432,5433,5436,5438,5441,5443],{"class":3418,"line":3706},[3416,5434,5435],{"class":3430},"        obj.",[3416,5437,4758],{"class":3447},[3416,5439,5440],{"class":3430},"[",[3416,5442,5378],{"class":3500},[3416,5444,5445],{"class":3430},"] = value\n",[3416,5447,5448],{"class":3418,"line":3743},[3416,5449,3491],{"emptyLinePlaceholder":3490},[3416,5451,5452],{"class":3418,"line":3756},[3416,5453,3491],{"emptyLinePlaceholder":3490},[3416,5455,5456,5458,5461],{"class":3418,"line":3782},[3416,5457,3423],{"class":3422},[3416,5459,5460],{"class":3426}," NonDataDescriptor",[3416,5462,3431],{"class":3430},[3416,5464,5465],{"class":3418,"line":4157},[3416,5466,5467],{"class":3500},"    \"\"\"Non-data descriptor — лише __get__, без __set__.\"\"\"\n",[3416,5469,5470],{"class":3418,"line":4184},[3416,5471,3491],{"emptyLinePlaceholder":3490},[3416,5473,5474,5476,5478,5480,5482,5484,5486,5488,5490,5492,5494],{"class":3418,"line":4190},[3416,5475,3437],{"class":3422},[3416,5477,4162],{"class":3440},[3416,5479,3444],{"class":3430},[3416,5481,3448],{"class":3447},[3416,5483,3451],{"class":3430},[3416,5485,3953],{"class":3447},[3416,5487,3451],{"class":3430},[3416,5489,3960],{"class":3447},[3416,5491,4177],{"class":3430},[3416,5493,3676],{"class":3422},[3416,5495,3463],{"class":3430},[3416,5497,5498,5500,5502,5504,5506],{"class":3418,"line":4206},[3416,5499,3684],{"class":3634},[3416,5501,4195],{"class":3430},[3416,5503,4198],{"class":3422},[3416,5505,4201],{"class":3422},[3416,5507,3431],{"class":3430},[3416,5509,5510,5512],{"class":3418,"line":4217},[3416,5511,4209],{"class":3634},[3416,5513,5361],{"class":3422},[3416,5515,5516,5518,5520,5522,5525],{"class":3418,"line":4238},[3416,5517,4241],{"class":3440},[3416,5519,3444],{"class":3430},[3416,5521,3717],{"class":3422},[3416,5523,5524],{"class":3500},"\"  [NonDataDescriptor.__get__] повертаю 'дескриптор'\"",[3416,5526,3504],{"class":3430},[3416,5528,5529,5531],{"class":3418,"line":4286},[3416,5530,3635],{"class":3634},[3416,5532,5533],{"class":3500}," \"значення від дескриптора\"\n",[3416,5535,5536],{"class":3418,"line":4294},[3416,5537,3491],{"emptyLinePlaceholder":3490},[3416,5539,5540],{"class":3418,"line":4299},[3416,5541,3491],{"emptyLinePlaceholder":3490},[3416,5543,5544,5546,5549],{"class":3418,"line":4325},[3416,5545,3423],{"class":3422},[3416,5547,5548],{"class":3426}," MyClass",[3416,5550,3431],{"class":3430},[3416,5552,5553],{"class":3418,"line":4368},[3416,5554,5555],{"class":3430},"    data_attr     = DataDescriptor()\n",[3416,5557,5558],{"class":3418,"line":4381},[3416,5559,5560],{"class":3430},"    nondata_attr  = NonDataDescriptor()\n",[3416,5562,5563],{"class":3418,"line":4386},[3416,5564,3491],{"emptyLinePlaceholder":3490},[3416,5566,5567],{"class":3418,"line":4391},[3416,5568,3491],{"emptyLinePlaceholder":3490},[3416,5570,5571],{"class":3418,"line":4401},[3416,5572,5573],{"class":3430},"obj = MyClass()\n",[3416,5575,5576],{"class":3418,"line":4407},[3416,5577,3491],{"emptyLinePlaceholder":3490},[3416,5579,5580],{"class":3418,"line":4413},[3416,5581,5582],{"class":3516},"# === Experiment 1: Data Descriptor ===\n",[3416,5584,5585,5587,5589,5592],{"class":3418,"line":4419},[3416,5586,4570],{"class":3440},[3416,5588,3444],{"class":3430},[3416,5590,5591],{"class":3500},"\"=== Data Descriptor ===\"",[3416,5593,3504],{"class":3430},[3416,5595,5596,5598,5600,5602,5605,5607,5610,5612,5614,5617],{"class":3418,"line":4425},[3416,5597,4570],{"class":3440},[3416,5599,3444],{"class":3430},[3416,5601,3717],{"class":3422},[3416,5603,5604],{"class":3500},"\"До запису: ",[3416,5606,3723],{"class":3422},[3416,5608,5609],{"class":3430},"obj.data_attr",[3416,5611,3735],{"class":3422},[3416,5613,3738],{"class":3500},[3416,5615,5616],{"class":3430},")      ",[3416,5618,5619],{"class":3516},"# виклик __get__, значення відсутнє\n",[3416,5621,5622],{"class":3418,"line":4430},[3416,5623,3491],{"emptyLinePlaceholder":3490},[3416,5625,5626,5629,5632],{"class":3418,"line":4464},[3416,5627,5628],{"class":3430},"obj.data_attr = ",[3416,5630,5631],{"class":3483},"42",[3416,5633,5634],{"class":3516},"                         # → DataDescriptor.__set__\n",[3416,5636,5637,5639,5641,5643,5646,5648,5650,5652,5654,5657],{"class":3418,"line":4475},[3416,5638,4570],{"class":3440},[3416,5640,3444],{"class":3430},[3416,5642,3717],{"class":3422},[3416,5644,5645],{"class":3500},"\"Після запису: ",[3416,5647,3723],{"class":3422},[3416,5649,5609],{"class":3430},[3416,5651,3735],{"class":3422},[3416,5653,3738],{"class":3500},[3416,5655,5656],{"class":3430},")   ",[3416,5658,5659],{"class":3516},"# → DataDescriptor.__get__\n",[3416,5661,5662],{"class":3418,"line":4485},[3416,5663,3491],{"emptyLinePlaceholder":3490},[3416,5665,5666],{"class":3418,"line":4490},[3416,5667,5668],{"class":3516},"# Спроба \"обійти\" дескриптор — записати у __dict__ напряму\n",[3416,5670,5671,5674,5676,5678,5681,5684],{"class":3418,"line":4508},[3416,5672,5673],{"class":3430},"obj.",[3416,5675,4758],{"class":3447},[3416,5677,5440],{"class":3430},[3416,5679,5680],{"class":3500},"\"data_attr\"",[3416,5682,5683],{"class":3430},"] = ",[3416,5685,5686],{"class":3483},"9999\n",[3416,5688,5689,5691,5693,5695,5698,5700,5702,5704,5706],{"class":3418,"line":4541},[3416,5690,4570],{"class":3440},[3416,5692,3444],{"class":3430},[3416,5694,3717],{"class":3422},[3416,5696,5697],{"class":3500},"\"Після __dict__['data_attr']=9999: ",[3416,5699,3723],{"class":3422},[3416,5701,5609],{"class":3430},[3416,5703,3735],{"class":3422},[3416,5705,3738],{"class":3500},[3416,5707,3504],{"class":3430},[3416,5709,5710],{"class":3418,"line":4546},[3416,5711,5712],{"class":3516},"# Data descriptor ігнорує __dict__ і все одно викликає __get__!\n",[3416,5714,5715],{"class":3418,"line":4551},[3416,5716,3491],{"emptyLinePlaceholder":3490},[3416,5718,5719],{"class":3418,"line":4567},[3416,5720,5721],{"class":3516},"# === Experiment 2: Non-data Descriptor ===\n",[3416,5723,5724,5726,5728,5730,5732,5735],{"class":3418,"line":4576},[3416,5725,4570],{"class":3440},[3416,5727,3444],{"class":3430},[3416,5729,3738],{"class":3500},[3416,5731,4588],{"class":4587},[3416,5733,5734],{"class":3500},"=== Non-data Descriptor ===\"",[3416,5736,3504],{"class":3430},[3416,5738,5739,5741,5743,5745,5747,5749,5752,5754,5756,5758],{"class":3418,"line":4608},[3416,5740,4570],{"class":3440},[3416,5742,3444],{"class":3430},[3416,5744,3717],{"class":3422},[3416,5746,5604],{"class":3500},[3416,5748,3723],{"class":3422},[3416,5750,5751],{"class":3430},"obj.nondata_attr",[3416,5753,3735],{"class":3422},[3416,5755,3738],{"class":3500},[3416,5757,5656],{"class":3430},[3416,5759,5760],{"class":3516},"# → NonDataDescriptor.__get__\n",[3416,5762,5763],{"class":3418,"line":4613},[3416,5764,3491],{"emptyLinePlaceholder":3490},[3416,5766,5767],{"class":3418,"line":4619},[3416,5768,5769],{"class":3516},"# Запис через звичайне присвоєння — обходить дескриптор!\n",[3416,5771,5773,5776],{"class":3418,"line":5772},50,[3416,5774,5775],{"class":3430},"obj.nondata_attr = ",[3416,5777,5778],{"class":3500},"\"моє власне значення\"\n",[3416,5780,5782,5784,5786,5788,5790,5792,5794,5796,5798,5801],{"class":3418,"line":5781},51,[3416,5783,4570],{"class":3440},[3416,5785,3444],{"class":3430},[3416,5787,3717],{"class":3422},[3416,5789,5645],{"class":3500},[3416,5791,3723],{"class":3422},[3416,5793,5751],{"class":3430},[3416,5795,3735],{"class":3422},[3416,5797,3738],{"class":3500},[3416,5799,5800],{"class":3430},") ",[3416,5802,5803],{"class":3516},"# тепер читаємо з __dict__ екземпляра!\n",[3416,5805,5807],{"class":3418,"line":5806},52,[3416,5808,3491],{"emptyLinePlaceholder":3490},[3416,5810,5812,5814,5816,5818,5821,5823,5825,5827,5829,5831],{"class":3418,"line":5811},53,[3416,5813,4570],{"class":3440},[3416,5815,3444],{"class":3430},[3416,5817,3717],{"class":3422},[3416,5819,5820],{"class":3500},"\"obj.__dict__: ",[3416,5822,3723],{"class":3422},[3416,5824,5673],{"class":3430},[3416,5826,4758],{"class":3447},[3416,5828,3735],{"class":3422},[3416,5830,3738],{"class":3500},[3416,5832,3504],{"class":3430},[4645,5834,5836,5844,5848,5856,5863,5870,5882,5885,5889,5893,5900,5911],{"title":5835},"python data_vs_nondata.py",[4649,5837,5839,4657,5842],{"className":5838},[3418],[3416,5840,4656],{"className":5841},[4655],[3838,5843,5835],{},[4649,5845,5847],{"className":5846},[3418],"=== Data Descriptor ===",[4649,5849,5851,5852],{"className":5850},[3418],"До запису: ",[3416,5853,5855],{"className":5854},[4721],"значення не встановлено",[4649,5857,5859,5860],{"className":5858},[3418],"  [DataDescriptor.__set__] value=",[3416,5861,5631],{"className":5862},[4667],[4649,5864,5866,5867],{"className":5865},[3418],"Після запису: ",[3416,5868,5631],{"className":5869},[4683],[4649,5871,5873,5874,5877,5878],{"className":5872},[3418],"Після __dict__['data_attr']=9999: ",[3416,5875,5631],{"className":5876},[4683],"  ",[3416,5879,5881],{"className":5880},[4721],"# дескриптор перемагає!",[4649,5883],{"className":5884},[3418],[4649,5886,5888],{"className":5887},[3418],"=== Non-data Descriptor ===",[4649,5890,5892],{"className":5891},[3418],"  [NonDataDescriptor.__get__] повертаю 'дескриптор'",[4649,5894,5851,5896],{"className":5895},[3418],[3416,5897,5899],{"className":5898},[4667],"значення від дескриптора",[4649,5901,5866,5903,5877,5907],{"className":5902},[3418],[3416,5904,5906],{"className":5905},[4683],"моє власне значення",[3416,5908,5910],{"className":5909},[4721],"# __dict__ затінив дескриптор!",[4649,5912,5914,5915],{"className":5913},[3418],"obj.__dict__: ",[3416,5916,5918],{"className":5917},[4710],"{'nondata_attr': 'моє власне значення', '_dd_val': 42}",[3394,5920,5921,5922,5927,5928,4881],{},"Результат демонструє принципову відмінність: ",[3838,5923,5924,5925],{},"data descriptor перехоплює навіть пряме звернення до ",[3398,5926,4758],{},", тоді як non-data descriptor легко «затінюється» записом в ",[3398,5929,4758],{},[3850,5931,5933],{"id":5932},"чому-звичайні-методи-є-non-data-дескрипторами","Чому звичайні методи є non-data дескрипторами",[3394,5935,5936,5937,5940,5941,5943,5944,5947,5948,3550],{},"Функція у Python — це об'єкт, і клас ",[3398,5938,5939],{},"function"," визначає метод ",[3398,5942,3862],{},". Отже, функція є ",[3838,5945,5946],{},"non-data дескриптором",". Саме тому Python автоматично підставляє ",[3398,5949,3448],{},[3407,5951,5953],{"className":3409,"code":5952,"language":3411,"meta":3412,"style":3412},"class Dog:\n    def bark(self) -> str:\n        return f\"{self.name} гавкає!\"\n\n    def __init__(self, name: str):\n        self.name = name\n\nd = Dog(\"Рекс\")\n\n# Що відбувається при d.bark()?\n# 1. Python шукає 'bark' у type(d).__mro__ → знаходить функцію Dog.bark\n# 2. Dog.bark — non-data descriptor (є __get__, немає __set__)\n# 3. Перевіряє d.__dict__ — там 'bark' немає\n# 4. Викликає Dog.bark.__get__(d, Dog) → отримує \"зв'язаний метод\"\n# 5. Викликає зв'язаний метод → self автоматично = d\n\n# Доведемо це явно:\nraw_function = Dog.__dict__['bark']    # сама функція, без дескрипторної магії\nprint(type(raw_function))              # \u003Cclass 'function'>\nprint(type(d.bark))                    # \u003Cclass 'method'> ← результат __get__\n\n# Вони еквівалентні:\nprint(d.bark())                        # Рекс гавкає!\nprint(raw_function(d))                 # Рекс гавкає! (self передаємо вручну)\n\n# Дескриптор можна викликати явно:\nbound_method = raw_function.__get__(d, Dog)\nprint(bound_method())                  # Рекс гавкає!\n",[3398,5954,5955,5964,5981,5998,6002,6022,6028,6032,6042,6046,6051,6056,6061,6066,6071,6076,6080,6085,6103,6117,6131,6135,6140,6150,6160,6164,6169,6179],{"__ignoreMap":3412},[3416,5956,5957,5959,5962],{"class":3418,"line":3419},[3416,5958,3423],{"class":3422},[3416,5960,5961],{"class":3426}," Dog",[3416,5963,3431],{"class":3430},[3416,5965,5966,5968,5971,5973,5975,5977,5979],{"class":3418,"line":3434},[3416,5967,3437],{"class":3422},[3416,5969,5970],{"class":3440}," bark",[3416,5972,3444],{"class":3430},[3416,5974,3448],{"class":3447},[3416,5976,3624],{"class":3430},[3416,5978,3460],{"class":3426},[3416,5980,3431],{"class":3430},[3416,5982,5983,5985,5987,5989,5991,5993,5995],{"class":3418,"line":3466},[3416,5984,3635],{"class":3634},[3416,5986,4513],{"class":3422},[3416,5988,3738],{"class":3500},[3416,5990,4264],{"class":3422},[3416,5992,4521],{"class":3430},[3416,5994,3735],{"class":3422},[3416,5996,5997],{"class":3500}," гавкає!\"\n",[3416,5999,6000],{"class":3418,"line":3475},[3416,6001,3491],{"emptyLinePlaceholder":3490},[3416,6003,6004,6006,6008,6010,6012,6014,6016,6018,6020],{"class":3418,"line":3487},[3416,6005,3437],{"class":3422},[3416,6007,3441],{"class":3440},[3416,6009,3444],{"class":3430},[3416,6011,3448],{"class":3447},[3416,6013,3451],{"class":3430},[3416,6015,3454],{"class":3447},[3416,6017,3457],{"class":3430},[3416,6019,3460],{"class":3426},[3416,6021,3463],{"class":3430},[3416,6023,6024,6026],{"class":3418,"line":3494},[3416,6025,3469],{"class":3422},[3416,6027,3472],{"class":3430},[3416,6029,6030],{"class":3418,"line":3507},[3416,6031,3491],{"emptyLinePlaceholder":3490},[3416,6033,6034,6037,6040],{"class":3418,"line":3520},[3416,6035,6036],{"class":3430},"d = Dog(",[3416,6038,6039],{"class":3500},"\"Рекс\"",[3416,6041,3504],{"class":3430},[3416,6043,6044],{"class":3418,"line":3532},[3416,6045,3491],{"emptyLinePlaceholder":3490},[3416,6047,6048],{"class":3418,"line":3648},[3416,6049,6050],{"class":3516},"# Що відбувається при d.bark()?\n",[3416,6052,6053],{"class":3418,"line":3654},[3416,6054,6055],{"class":3516},"# 1. Python шукає 'bark' у type(d).__mro__ → знаходить функцію Dog.bark\n",[3416,6057,6058],{"class":3418,"line":3681},[3416,6059,6060],{"class":3516},"# 2. Dog.bark — non-data descriptor (є __get__, немає __set__)\n",[3416,6062,6063],{"class":3418,"line":3706},[3416,6064,6065],{"class":3516},"# 3. Перевіряє d.__dict__ — там 'bark' немає\n",[3416,6067,6068],{"class":3418,"line":3743},[3416,6069,6070],{"class":3516},"# 4. Викликає Dog.bark.__get__(d, Dog) → отримує \"зв'язаний метод\"\n",[3416,6072,6073],{"class":3418,"line":3756},[3416,6074,6075],{"class":3516},"# 5. Викликає зв'язаний метод → self автоматично = d\n",[3416,6077,6078],{"class":3418,"line":3782},[3416,6079,3491],{"emptyLinePlaceholder":3490},[3416,6081,6082],{"class":3418,"line":4157},[3416,6083,6084],{"class":3516},"# Доведемо це явно:\n",[3416,6086,6087,6090,6092,6094,6097,6100],{"class":3418,"line":4184},[3416,6088,6089],{"class":3430},"raw_function = Dog.",[3416,6091,4758],{"class":3447},[3416,6093,5440],{"class":3430},[3416,6095,6096],{"class":3500},"'bark'",[3416,6098,6099],{"class":3430},"]    ",[3416,6101,6102],{"class":3516},"# сама функція, без дескрипторної магії\n",[3416,6104,6105,6107,6109,6111,6114],{"class":3418,"line":4190},[3416,6106,4570],{"class":3440},[3416,6108,3444],{"class":3430},[3416,6110,3726],{"class":3426},[3416,6112,6113],{"class":3430},"(raw_function))              ",[3416,6115,6116],{"class":3516},"# \u003Cclass 'function'>\n",[3416,6118,6119,6121,6123,6125,6128],{"class":3418,"line":4206},[3416,6120,4570],{"class":3440},[3416,6122,3444],{"class":3430},[3416,6124,3726],{"class":3426},[3416,6126,6127],{"class":3430},"(d.bark))                    ",[3416,6129,6130],{"class":3516},"# \u003Cclass 'method'> ← результат __get__\n",[3416,6132,6133],{"class":3418,"line":4217},[3416,6134,3491],{"emptyLinePlaceholder":3490},[3416,6136,6137],{"class":3418,"line":4238},[3416,6138,6139],{"class":3516},"# Вони еквівалентні:\n",[3416,6141,6142,6144,6147],{"class":3418,"line":4286},[3416,6143,4570],{"class":3440},[3416,6145,6146],{"class":3430},"(d.bark())                        ",[3416,6148,6149],{"class":3516},"# Рекс гавкає!\n",[3416,6151,6152,6154,6157],{"class":3418,"line":4294},[3416,6153,4570],{"class":3440},[3416,6155,6156],{"class":3430},"(raw_function(d))                 ",[3416,6158,6159],{"class":3516},"# Рекс гавкає! (self передаємо вручну)\n",[3416,6161,6162],{"class":3418,"line":4299},[3416,6163,3491],{"emptyLinePlaceholder":3490},[3416,6165,6166],{"class":3418,"line":4325},[3416,6167,6168],{"class":3516},"# Дескриптор можна викликати явно:\n",[3416,6170,6171,6174,6176],{"class":3418,"line":4368},[3416,6172,6173],{"class":3430},"bound_method = raw_function.",[3416,6175,3862],{"class":3440},[3416,6177,6178],{"class":3430},"(d, Dog)\n",[3416,6180,6181,6183,6186],{"class":3418,"line":4381},[3416,6182,4570],{"class":3440},[3416,6184,6185],{"class":3430},"(bound_method())                  ",[3416,6187,6149],{"class":3516},[6189,6190,6191,6192,6195,6196,6199,6200,6203,6204,6206],"tip",{},"Розуміння того, що ",[3838,6193,6194],{},"методи — це non-data дескриптори",", пояснює, чому ",[3398,6197,6198],{},"obj.method = lambda: \"перевизначено\""," працює: ви просто записуєте нове значення в ",[3398,6201,6202],{},"obj.__dict__['method']",", яке затіняє дескриптор із класу. Data descriptor (наприклад, ",[3398,6205,3549],{},") так обійти неможливо.",[3850,6208,6210,6211,6213],{"id":6209},"підтвердження-property-як-data-descriptor","Підтвердження: ",[3398,6212,3549],{}," як data descriptor",[3394,6215,6216,6218],{},[3398,6217,3549],{}," — це вбудований клас Python. Подивимось, що він визначає:",[3407,6220,6222],{"className":3409,"code":6221,"language":3411,"meta":3412,"style":3412},"# property — це data descriptor: має __get__, __set__ і __delete__\nprint(hasattr(property, '__get__'))     # True\nprint(hasattr(property, '__set__'))     # True  ← саме тому це data descriptor\nprint(hasattr(property, '__delete__'))  # True\n\nclass Circle:\n    def __init__(self, radius: float):\n        self._radius = radius\n\n    @property\n    def radius(self) -> float:\n        return self._radius\n\n    @radius.setter\n    def radius(self, value: float) -> None:\n        if value \u003C 0:\n            raise ValueError(\"Радіус не може бути від'ємним\")\n        self._radius = value\n\n\nc = Circle(5.0)\n\n# Спроба обійти @property через __dict__:\nc.__dict__['radius'] = -100  # записуємо напряму у словник екземпляра\n\n# Але @property (data descriptor) ігнорує __dict__!\nprint(c.radius)          # 5.0 — повертає self._radius, а не -100\nprint(c.__dict__)        # {'_radius': 5.0, 'radius': -100} — запис є, але ігнорується\n",[3398,6223,6224,6229,6254,6276,6298,6302,6311,6332,6339,6343,6349,6366,6375,6379,6384,6408,6420,6433,6440,6444,6448,6458,6462,6467,6488,6492,6497,6507],{"__ignoreMap":3412},[3416,6225,6226],{"class":3418,"line":3419},[3416,6227,6228],{"class":3516},"# property — це data descriptor: має __get__, __set__ і __delete__\n",[3416,6230,6231,6233,6235,6238,6240,6243,6245,6248,6251],{"class":3418,"line":3434},[3416,6232,4570],{"class":3440},[3416,6234,3444],{"class":3430},[3416,6236,6237],{"class":3440},"hasattr",[3416,6239,3444],{"class":3430},[3416,6241,6242],{"class":3426},"property",[3416,6244,3451],{"class":3430},[3416,6246,6247],{"class":3500},"'__get__'",[3416,6249,6250],{"class":3430},"))     ",[3416,6252,6253],{"class":3516},"# True\n",[3416,6255,6256,6258,6260,6262,6264,6266,6268,6271,6273],{"class":3418,"line":3466},[3416,6257,4570],{"class":3440},[3416,6259,3444],{"class":3430},[3416,6261,6237],{"class":3440},[3416,6263,3444],{"class":3430},[3416,6265,6242],{"class":3426},[3416,6267,3451],{"class":3430},[3416,6269,6270],{"class":3500},"'__set__'",[3416,6272,6250],{"class":3430},[3416,6274,6275],{"class":3516},"# True  ← саме тому це data descriptor\n",[3416,6277,6278,6280,6282,6284,6286,6288,6290,6293,6296],{"class":3418,"line":3475},[3416,6279,4570],{"class":3440},[3416,6281,3444],{"class":3430},[3416,6283,6237],{"class":3440},[3416,6285,3444],{"class":3430},[3416,6287,6242],{"class":3426},[3416,6289,3451],{"class":3430},[3416,6291,6292],{"class":3500},"'__delete__'",[3416,6294,6295],{"class":3430},"))  ",[3416,6297,6253],{"class":3516},[3416,6299,6300],{"class":3418,"line":3487},[3416,6301,3491],{"emptyLinePlaceholder":3490},[3416,6303,6304,6306,6309],{"class":3418,"line":3494},[3416,6305,3423],{"class":3422},[3416,6307,6308],{"class":3426}," Circle",[3416,6310,3431],{"class":3430},[3416,6312,6313,6315,6317,6319,6321,6323,6326,6328,6330],{"class":3418,"line":3507},[3416,6314,3437],{"class":3422},[3416,6316,3441],{"class":3440},[3416,6318,3444],{"class":3430},[3416,6320,3448],{"class":3447},[3416,6322,3451],{"class":3430},[3416,6324,6325],{"class":3447},"radius",[3416,6327,3457],{"class":3430},[3416,6329,3627],{"class":3426},[3416,6331,3463],{"class":3430},[3416,6333,6334,6336],{"class":3418,"line":3520},[3416,6335,3469],{"class":3422},[3416,6337,6338],{"class":3430},"._radius = radius\n",[3416,6340,6341],{"class":3418,"line":3532},[3416,6342,3491],{"emptyLinePlaceholder":3490},[3416,6344,6345,6347],{"class":3418,"line":3648},[3416,6346,3607],{"class":3440},[3416,6348,3610],{"class":3426},[3416,6350,6351,6353,6356,6358,6360,6362,6364],{"class":3418,"line":3654},[3416,6352,3437],{"class":3422},[3416,6354,6355],{"class":3440}," radius",[3416,6357,3444],{"class":3430},[3416,6359,3448],{"class":3447},[3416,6361,3624],{"class":3430},[3416,6363,3627],{"class":3426},[3416,6365,3431],{"class":3430},[3416,6367,6368,6370,6372],{"class":3418,"line":3681},[3416,6369,3635],{"class":3634},[3416,6371,3638],{"class":3422},[3416,6373,6374],{"class":3430},"._radius\n",[3416,6376,6377],{"class":3418,"line":3706},[3416,6378,3491],{"emptyLinePlaceholder":3490},[3416,6380,6381],{"class":3418,"line":3743},[3416,6382,6383],{"class":3440},"    @radius.setter\n",[3416,6385,6386,6388,6390,6392,6394,6396,6398,6400,6402,6404,6406],{"class":3418,"line":3756},[3416,6387,3437],{"class":3422},[3416,6389,6355],{"class":3440},[3416,6391,3444],{"class":3430},[3416,6393,3448],{"class":3447},[3416,6395,3451],{"class":3430},[3416,6397,3667],{"class":3447},[3416,6399,3457],{"class":3430},[3416,6401,3627],{"class":3426},[3416,6403,3624],{"class":3430},[3416,6405,3676],{"class":3422},[3416,6407,3431],{"class":3430},[3416,6409,6410,6412,6415,6418],{"class":3418,"line":3782},[3416,6411,3684],{"class":3634},[3416,6413,6414],{"class":3430}," value \u003C ",[3416,6416,6417],{"class":3483},"0",[3416,6419,3431],{"class":3430},[3416,6421,6422,6424,6426,6428,6431],{"class":3418,"line":4157},[3416,6423,3709],{"class":3634},[3416,6425,3761],{"class":3426},[3416,6427,3444],{"class":3430},[3416,6429,6430],{"class":3500},"\"Радіус не може бути від'ємним\"",[3416,6432,3504],{"class":3430},[3416,6434,6435,6437],{"class":3418,"line":4184},[3416,6436,3469],{"class":3422},[3416,6438,6439],{"class":3430},"._radius = value\n",[3416,6441,6442],{"class":3418,"line":4190},[3416,6443,3491],{"emptyLinePlaceholder":3490},[3416,6445,6446],{"class":3418,"line":4206},[3416,6447,3491],{"emptyLinePlaceholder":3490},[3416,6449,6450,6453,6456],{"class":3418,"line":4217},[3416,6451,6452],{"class":3430},"c = Circle(",[3416,6454,6455],{"class":3483},"5.0",[3416,6457,3504],{"class":3430},[3416,6459,6460],{"class":3418,"line":4238},[3416,6461,3491],{"emptyLinePlaceholder":3490},[3416,6463,6464],{"class":3418,"line":4286},[3416,6465,6466],{"class":3516},"# Спроба обійти @property через __dict__:\n",[3416,6468,6469,6472,6474,6476,6479,6482,6485],{"class":3418,"line":4294},[3416,6470,6471],{"class":3430},"c.",[3416,6473,4758],{"class":3447},[3416,6475,5440],{"class":3430},[3416,6477,6478],{"class":3500},"'radius'",[3416,6480,6481],{"class":3430},"] = -",[3416,6483,6484],{"class":3483},"100",[3416,6486,6487],{"class":3516},"  # записуємо напряму у словник екземпляра\n",[3416,6489,6490],{"class":3418,"line":4299},[3416,6491,3491],{"emptyLinePlaceholder":3490},[3416,6493,6494],{"class":3418,"line":4325},[3416,6495,6496],{"class":3516},"# Але @property (data descriptor) ігнорує __dict__!\n",[3416,6498,6499,6501,6504],{"class":3418,"line":4368},[3416,6500,4570],{"class":3440},[3416,6502,6503],{"class":3430},"(c.radius)          ",[3416,6505,6506],{"class":3516},"# 5.0 — повертає self._radius, а не -100\n",[3416,6508,6509,6511,6514,6516,6519],{"class":3418,"line":4381},[3416,6510,4570],{"class":3440},[3416,6512,6513],{"class":3430},"(c.",[3416,6515,4758],{"class":3447},[3416,6517,6518],{"class":3430},")        ",[3416,6520,6521],{"class":3516},"# {'_radius': 5.0, 'radius': -100} — запис є, але ігнорується\n",[3394,6523,6524,6525,6527,6528,6530,6531,3986],{},"Саме тому ",[3398,6526,3549],{}," не можна «перезаписати» через присвоєння ",[3398,6529,3972],{}," без сеттера — дескриптор перехоплює запис і або делегує сеттеру, або (якщо сеттер не визначений) підіймає ",[3398,6532,6533],{},"AttributeError: can't set attribute",[3843,6535],{},[3389,6537,6539],{"id":6538},"частина-iii-практичний-сценарій-1-валідація-типів-та-діапазонів","Частина III: Практичний сценарій 1 — Валідація типів та діапазонів",[3850,6541,6543],{"id":6542},"проблема-повторювана-валідація-полів","Проблема: повторювана валідація полів",[3394,6545,6546],{},"Повернімося до задачі, з якої ми починали. Тепер ми маємо повний інструментарій, щоб вирішити її елегантно. Побудуємо ієрархію дескрипторів-валідаторів, яку можна перевикористовувати у довільній кількості класів.",[3394,6548,6549],{},"Почнемо з абстрактного базового класу для всіх валідаторів:",[3407,6551,6553],{"className":3409,"code":6552,"language":3411,"meta":3412,"style":3412},"# validators.py\nfrom abc import ABC, abstractmethod\n\n\nclass Validator(ABC):\n    \"\"\"\n    Абстрактний базовий клас для дескрипторів-валідаторів.\n    Реалізує загальний протокол __get__\u002F__set__\u002F__set_name__,\n    делегуючи специфічну логіку перевірки методу validate().\n    \"\"\"\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, obj, objtype=None):\n        if obj is None:\n            return self\n        return getattr(obj, self.private_name, None)\n\n    def __set__(self, obj, value) -> None:\n        # Спочатку валідуємо, потім зберігаємо\n        self.validate(value)\n        setattr(obj, self.private_name, value)\n\n    @abstractmethod\n    def validate(self, value) -> None:\n        \"\"\"\n        Перевіряє значення. Має підняти TypeError або ValueError\n        при невалідному вводі.\n        \"\"\"\n        ...\n",[3398,6554,6555,6560,6574,6578,6582,6592,6596,6601,6606,6611,6615,6619,6651,6658,6677,6681,6705,6717,6723,6740,6744,6768,6773,6780,6790,6794,6799,6820,6825,6830,6835,6839],{"__ignoreMap":3412},[3416,6556,6557],{"class":3418,"line":3419},[3416,6558,6559],{"class":3516},"# validators.py\n",[3416,6561,6562,6565,6568,6571],{"class":3418,"line":3434},[3416,6563,6564],{"class":3634},"from",[3416,6566,6567],{"class":3430}," abc ",[3416,6569,6570],{"class":3634},"import",[3416,6572,6573],{"class":3430}," ABC, abstractmethod\n",[3416,6575,6576],{"class":3418,"line":3466},[3416,6577,3491],{"emptyLinePlaceholder":3490},[3416,6579,6580],{"class":3418,"line":3475},[3416,6581,3491],{"emptyLinePlaceholder":3490},[3416,6583,6584,6586,6589],{"class":3418,"line":3487},[3416,6585,3423],{"class":3422},[3416,6587,6588],{"class":3426}," Validator",[3416,6590,6591],{"class":3430},"(ABC):\n",[3416,6593,6594],{"class":3418,"line":3494},[3416,6595,4046],{"class":3500},[3416,6597,6598],{"class":3418,"line":3507},[3416,6599,6600],{"class":3500},"    Абстрактний базовий клас для дескрипторів-валідаторів.\n",[3416,6602,6603],{"class":3418,"line":3520},[3416,6604,6605],{"class":3500},"    Реалізує загальний протокол __get__\u002F__set__\u002F__set_name__,\n",[3416,6607,6608],{"class":3418,"line":3532},[3416,6609,6610],{"class":3500},"    делегуючи специфічну логіку перевірки методу validate().\n",[3416,6612,6613],{"class":3418,"line":3648},[3416,6614,4046],{"class":3500},[3416,6616,6617],{"class":3418,"line":3654},[3416,6618,3491],{"emptyLinePlaceholder":3490},[3416,6620,6621,6623,6625,6627,6629,6631,6633,6635,6637,6639,6641,6643,6645,6647,6649],{"class":3418,"line":3681},[3416,6622,3437],{"class":3422},[3416,6624,4076],{"class":3440},[3416,6626,3444],{"class":3430},[3416,6628,3448],{"class":3447},[3416,6630,3451],{"class":3430},[3416,6632,4007],{"class":3447},[3416,6634,3457],{"class":3430},[3416,6636,3726],{"class":3426},[3416,6638,3451],{"class":3430},[3416,6640,3454],{"class":3447},[3416,6642,3457],{"class":3430},[3416,6644,3460],{"class":3426},[3416,6646,3624],{"class":3430},[3416,6648,3676],{"class":3422},[3416,6650,3431],{"class":3430},[3416,6652,6653,6655],{"class":3418,"line":3706},[3416,6654,3469],{"class":3422},[3416,6656,6657],{"class":3430},".public_name  = name\n",[3416,6659,6660,6662,6664,6666,6668,6670,6672,6674],{"class":3418,"line":3743},[3416,6661,3469],{"class":3422},[3416,6663,4134],{"class":3430},[3416,6665,3717],{"class":3422},[3416,6667,4139],{"class":3500},[3416,6669,3723],{"class":3422},[3416,6671,3454],{"class":3430},[3416,6673,3735],{"class":3422},[3416,6675,6676],{"class":3500},"\"\n",[3416,6678,6679],{"class":3418,"line":3756},[3416,6680,3491],{"emptyLinePlaceholder":3490},[3416,6682,6683,6685,6687,6689,6691,6693,6695,6697,6699,6701,6703],{"class":3418,"line":3782},[3416,6684,3437],{"class":3422},[3416,6686,4162],{"class":3440},[3416,6688,3444],{"class":3430},[3416,6690,3448],{"class":3447},[3416,6692,3451],{"class":3430},[3416,6694,3953],{"class":3447},[3416,6696,3451],{"class":3430},[3416,6698,3960],{"class":3447},[3416,6700,4177],{"class":3430},[3416,6702,3676],{"class":3422},[3416,6704,3463],{"class":3430},[3416,6706,6707,6709,6711,6713,6715],{"class":3418,"line":4157},[3416,6708,3684],{"class":3634},[3416,6710,4195],{"class":3430},[3416,6712,4198],{"class":3422},[3416,6714,4201],{"class":3422},[3416,6716,3431],{"class":3430},[3416,6718,6719,6721],{"class":3418,"line":4184},[3416,6720,4209],{"class":3634},[3416,6722,5361],{"class":3422},[3416,6724,6725,6727,6730,6732,6734,6736,6738],{"class":3418,"line":4190},[3416,6726,3635],{"class":3634},[3416,6728,6729],{"class":3440}," getattr",[3416,6731,4226],{"class":3430},[3416,6733,3448],{"class":3422},[3416,6735,4231],{"class":3430},[3416,6737,3676],{"class":3422},[3416,6739,3504],{"class":3430},[3416,6741,6742],{"class":3418,"line":4206},[3416,6743,3491],{"emptyLinePlaceholder":3490},[3416,6745,6746,6748,6750,6752,6754,6756,6758,6760,6762,6764,6766],{"class":3418,"line":4217},[3416,6747,3437],{"class":3422},[3416,6749,4304],{"class":3440},[3416,6751,3444],{"class":3430},[3416,6753,3448],{"class":3447},[3416,6755,3451],{"class":3430},[3416,6757,3953],{"class":3447},[3416,6759,3451],{"class":3430},[3416,6761,3667],{"class":3447},[3416,6763,3624],{"class":3430},[3416,6765,3676],{"class":3422},[3416,6767,3431],{"class":3430},[3416,6769,6770],{"class":3418,"line":4238},[3416,6771,6772],{"class":3516},"        # Спочатку валідуємо, потім зберігаємо\n",[3416,6774,6775,6777],{"class":3418,"line":4286},[3416,6776,3469],{"class":3422},[3416,6778,6779],{"class":3430},".validate(value)\n",[3416,6781,6782,6784,6786,6788],{"class":3418,"line":4294},[3416,6783,4371],{"class":3440},[3416,6785,4226],{"class":3430},[3416,6787,3448],{"class":3422},[3416,6789,4378],{"class":3430},[3416,6791,6792],{"class":3418,"line":4299},[3416,6793,3491],{"emptyLinePlaceholder":3490},[3416,6795,6796],{"class":3418,"line":4325},[3416,6797,6798],{"class":3440},"    @abstractmethod\n",[3416,6800,6801,6803,6806,6808,6810,6812,6814,6816,6818],{"class":3418,"line":4368},[3416,6802,3437],{"class":3422},[3416,6804,6805],{"class":3440}," validate",[3416,6807,3444],{"class":3430},[3416,6809,3448],{"class":3447},[3416,6811,3451],{"class":3430},[3416,6813,3667],{"class":3447},[3416,6815,3624],{"class":3430},[3416,6817,3676],{"class":3422},[3416,6819,3431],{"class":3430},[3416,6821,6822],{"class":3418,"line":4381},[3416,6823,6824],{"class":3500},"        \"\"\"\n",[3416,6826,6827],{"class":3418,"line":4386},[3416,6828,6829],{"class":3500},"        Перевіряє значення. Має підняти TypeError або ValueError\n",[3416,6831,6832],{"class":3418,"line":4391},[3416,6833,6834],{"class":3500},"        при невалідному вводі.\n",[3416,6836,6837],{"class":3418,"line":4401},[3416,6838,6824],{"class":3500},[3416,6840,6841],{"class":3418,"line":4407},[3416,6842,6843],{"class":3430},"        ...\n",[3394,6845,6846,6847,3550],{},"Маючи базовий клас, будуємо конкретні валідатори через успадкування — кожен реалізує лише ",[3398,6848,6849],{},"validate()",[3407,6851,6853],{"className":3409,"code":6852,"language":3411,"meta":3412,"style":3412},"# validators.py (продовження)\n\nclass TypedField(Validator):\n    \"\"\"\n    Дескриптор: перевіряє тип значення.\n    \n    name = TypedField(str)         # лише рядки\n    count = TypedField((int, float))  # int або float\n    \"\"\"\n\n    def __init__(self, expected_type: type | tuple):\n        self.expected_type = expected_type\n\n    def validate(self, value) -> None:\n        if not isinstance(value, self.expected_type):\n            type_name = (\n                self.expected_type.__name__\n                if isinstance(self.expected_type, type)\n                else \" | \".join(t.__name__ for t in self.expected_type)\n            )\n            raise TypeError(\n                f\"Атрибут '{self.public_name}' очікує тип {type_name}, \"\n                f\"отримано {type(value).__name__!r}: {value!r}\"\n            )\n\n\nclass RangedField(Validator):\n    \"\"\"\n    Дескриптор: перевіряє числове значення на входження в діапазон [min_val, max_val].\n    None означає «без обмеження» з відповідного боку.\n    \n    temperature = RangedField(-273.15, 1_000_000)\n    percentage  = RangedField(0.0, 100.0)\n    \"\"\"\n\n    def __init__(\n        self,\n        min_val: float | None = None,\n        max_val: float | None = None,\n        *,\n        inclusive: bool = True,\n    ):\n        self.min_val   = min_val\n        self.max_val   = max_val\n        self.inclusive = inclusive\n\n    def validate(self, 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        too_low  = (self.min_val is not None and\n                    (value \u003C self.min_val if self.inclusive else value \u003C= self.min_val))\n        too_high = (self.max_val is not None and\n                    (value > self.max_val if self.inclusive else value >= self.max_val))\n        if too_low or too_high:\n            bound = f\"[{self.min_val}, {self.max_val}]\" if self.inclusive else f\"({self.min_val}, {self.max_val})\"\n            raise ValueError(\n                f\"Атрибут '{self.public_name}' = {value} виходить за межі {bound}\"\n            )\n\n\nclass NonEmptyString(Validator):\n    \"\"\"\n    Дескриптор: гарантує, що рядок непустий після strip().\n    Опціонально обмежує максимальну довжину.\n    \n    title       = NonEmptyString()\n    description = NonEmptyString(max_length=500)\n    \"\"\"\n\n    def __init__(self, max_length: int | None = None):\n        self.max_length = max_length\n\n    def validate(self, value) -> None:\n        if not isinstance(value, str):\n            raise TypeError(\n                f\"Атрибут '{self.public_name}' має бути рядком, \"\n                f\"отримано {type(value).__name__!r}\"\n            )\n        stripped = value.strip()\n        if not stripped:\n            raise ValueError(\n                f\"Атрибут '{self.public_name}' не може бути порожнім рядком\"\n            )\n        if self.max_length is not None and len(stripped) > self.max_length:\n            raise ValueError(\n                f\"Атрибут '{self.public_name}' перевищує максимальну довжину \"\n                f\"{self.max_length} символів (фактично: {len(stripped)})\"\n            )\n",[3398,6854,6855,6860,6864,6878,6882,6887,6892,6897,6902,6906,6910,6937,6944,6948,6968,6984,6989,7000,7018,7045,7050,7059,7086,7113,7117,7121,7125,7138,7142,7147,7152,7156,7161,7166,7170,7174,7182,7189,7208,7227,7232,7249,7254,7261,7268,7275,7279,7299,7317,7325,7340,7358,7362,7381,7410,7429,7455,7469,7530,7539,7573,7578,7583,7588,7602,7607,7613,7619,7624,7630,7636,7641,7646,7676,7684,7689,7710,7725,7734,7750,7769,7774,7780,7790,7799,7815,7820,7850,7859,7875,7904],{"__ignoreMap":3412},[3416,6856,6857],{"class":3418,"line":3419},[3416,6858,6859],{"class":3516},"# validators.py (продовження)\n",[3416,6861,6862],{"class":3418,"line":3434},[3416,6863,3491],{"emptyLinePlaceholder":3490},[3416,6865,6866,6868,6871,6873,6876],{"class":3418,"line":3466},[3416,6867,3423],{"class":3422},[3416,6869,6870],{"class":3426}," TypedField",[3416,6872,3444],{"class":3430},[3416,6874,6875],{"class":3426},"Validator",[3416,6877,3463],{"class":3430},[3416,6879,6880],{"class":3418,"line":3475},[3416,6881,4046],{"class":3500},[3416,6883,6884],{"class":3418,"line":3487},[3416,6885,6886],{"class":3500},"    Дескриптор: перевіряє тип значення.\n",[3416,6888,6889],{"class":3418,"line":3494},[3416,6890,6891],{"class":3500},"    \n",[3416,6893,6894],{"class":3418,"line":3507},[3416,6895,6896],{"class":3500},"    name = TypedField(str)         # лише рядки\n",[3416,6898,6899],{"class":3418,"line":3520},[3416,6900,6901],{"class":3500},"    count = TypedField((int, float))  # int або float\n",[3416,6903,6904],{"class":3418,"line":3532},[3416,6905,4046],{"class":3500},[3416,6907,6908],{"class":3418,"line":3648},[3416,6909,3491],{"emptyLinePlaceholder":3490},[3416,6911,6912,6914,6916,6918,6920,6922,6925,6927,6929,6932,6935],{"class":3418,"line":3654},[3416,6913,3437],{"class":3422},[3416,6915,3441],{"class":3440},[3416,6917,3444],{"class":3430},[3416,6919,3448],{"class":3447},[3416,6921,3451],{"class":3430},[3416,6923,6924],{"class":3447},"expected_type",[3416,6926,3457],{"class":3430},[3416,6928,3726],{"class":3426},[3416,6930,6931],{"class":3430}," | ",[3416,6933,6934],{"class":3426},"tuple",[3416,6936,3463],{"class":3430},[3416,6938,6939,6941],{"class":3418,"line":3681},[3416,6940,3469],{"class":3422},[3416,6942,6943],{"class":3430},".expected_type = expected_type\n",[3416,6945,6946],{"class":3418,"line":3706},[3416,6947,3491],{"emptyLinePlaceholder":3490},[3416,6949,6950,6952,6954,6956,6958,6960,6962,6964,6966],{"class":3418,"line":3743},[3416,6951,3437],{"class":3422},[3416,6953,6805],{"class":3440},[3416,6955,3444],{"class":3430},[3416,6957,3448],{"class":3447},[3416,6959,3451],{"class":3430},[3416,6961,3667],{"class":3447},[3416,6963,3624],{"class":3430},[3416,6965,3676],{"class":3422},[3416,6967,3431],{"class":3430},[3416,6969,6970,6972,6974,6976,6979,6981],{"class":3418,"line":3756},[3416,6971,3684],{"class":3634},[3416,6973,3687],{"class":3422},[3416,6975,3690],{"class":3440},[3416,6977,6978],{"class":3430},"(value, ",[3416,6980,3448],{"class":3422},[3416,6982,6983],{"class":3430},".expected_type):\n",[3416,6985,6986],{"class":3418,"line":3782},[3416,6987,6988],{"class":3430},"            type_name = (\n",[3416,6990,6991,6994,6997],{"class":3418,"line":4157},[3416,6992,6993],{"class":3422},"                self",[3416,6995,6996],{"class":3430},".expected_type.",[3416,6998,6999],{"class":3447},"__name__\n",[3416,7001,7002,7005,7007,7009,7011,7014,7016],{"class":3418,"line":4184},[3416,7003,7004],{"class":3634},"                if",[3416,7006,3690],{"class":3440},[3416,7008,3444],{"class":3430},[3416,7010,3448],{"class":3422},[3416,7012,7013],{"class":3430},".expected_type, ",[3416,7015,3726],{"class":3426},[3416,7017,3504],{"class":3430},[3416,7019,7020,7023,7026,7029,7031,7034,7037,7040,7042],{"class":3418,"line":4190},[3416,7021,7022],{"class":3634},"                else",[3416,7024,7025],{"class":3500}," \" | \"",[3416,7027,7028],{"class":3430},".join(t.",[3416,7030,3732],{"class":3447},[3416,7032,7033],{"class":3634}," for",[3416,7035,7036],{"class":3430}," t ",[3416,7038,7039],{"class":3634},"in",[3416,7041,3638],{"class":3422},[3416,7043,7044],{"class":3430},".expected_type)\n",[3416,7046,7047],{"class":3418,"line":4206},[3416,7048,7049],{"class":3430},"            )\n",[3416,7051,7052,7054,7056],{"class":3418,"line":4217},[3416,7053,3709],{"class":3634},[3416,7055,3712],{"class":3426},[3416,7057,7058],{"class":3430},"(\n",[3416,7060,7061,7064,7067,7069,7071,7073,7076,7078,7081,7083],{"class":3418,"line":4238},[3416,7062,7063],{"class":3422},"                f",[3416,7065,7066],{"class":3500},"\"Атрибут '",[3416,7068,4264],{"class":3422},[3416,7070,4267],{"class":3430},[3416,7072,3735],{"class":3422},[3416,7074,7075],{"class":3500},"' очікує тип ",[3416,7077,3723],{"class":3422},[3416,7079,7080],{"class":3430},"type_name",[3416,7082,3735],{"class":3422},[3416,7084,7085],{"class":3500},", \"\n",[3416,7087,7088,7090,7093,7095,7097,7099,7101,7103,7105,7107,7109,7111],{"class":3418,"line":4286},[3416,7089,7063],{"class":3422},[3416,7091,7092],{"class":3500},"\"отримано ",[3416,7094,3723],{"class":3422},[3416,7096,3726],{"class":3426},[3416,7098,3729],{"class":3430},[3416,7100,3732],{"class":3447},[3416,7102,4279],{"class":3422},[3416,7104,3457],{"class":3500},[3416,7106,3723],{"class":3422},[3416,7108,3667],{"class":3430},[3416,7110,4279],{"class":3422},[3416,7112,6676],{"class":3500},[3416,7114,7115],{"class":3418,"line":4294},[3416,7116,7049],{"class":3430},[3416,7118,7119],{"class":3418,"line":4299},[3416,7120,3491],{"emptyLinePlaceholder":3490},[3416,7122,7123],{"class":3418,"line":4325},[3416,7124,3491],{"emptyLinePlaceholder":3490},[3416,7126,7127,7129,7132,7134,7136],{"class":3418,"line":4368},[3416,7128,3423],{"class":3422},[3416,7130,7131],{"class":3426}," RangedField",[3416,7133,3444],{"class":3430},[3416,7135,6875],{"class":3426},[3416,7137,3463],{"class":3430},[3416,7139,7140],{"class":3418,"line":4381},[3416,7141,4046],{"class":3500},[3416,7143,7144],{"class":3418,"line":4386},[3416,7145,7146],{"class":3500},"    Дескриптор: перевіряє числове значення на входження в діапазон [min_val, max_val].\n",[3416,7148,7149],{"class":3418,"line":4391},[3416,7150,7151],{"class":3500},"    None означає «без обмеження» з відповідного боку.\n",[3416,7153,7154],{"class":3418,"line":4401},[3416,7155,6891],{"class":3500},[3416,7157,7158],{"class":3418,"line":4407},[3416,7159,7160],{"class":3500},"    temperature = RangedField(-273.15, 1_000_000)\n",[3416,7162,7163],{"class":3418,"line":4413},[3416,7164,7165],{"class":3500},"    percentage  = RangedField(0.0, 100.0)\n",[3416,7167,7168],{"class":3418,"line":4419},[3416,7169,4046],{"class":3500},[3416,7171,7172],{"class":3418,"line":4425},[3416,7173,3491],{"emptyLinePlaceholder":3490},[3416,7175,7176,7178,7180],{"class":3418,"line":4430},[3416,7177,3437],{"class":3422},[3416,7179,3441],{"class":3440},[3416,7181,7058],{"class":3430},[3416,7183,7184,7186],{"class":3418,"line":4464},[3416,7185,3469],{"class":3447},[3416,7187,7188],{"class":3430},",\n",[3416,7190,7191,7194,7196,7198,7200,7202,7204,7206],{"class":3418,"line":4475},[3416,7192,7193],{"class":3447},"        min_val",[3416,7195,3457],{"class":3430},[3416,7197,3627],{"class":3426},[3416,7199,6931],{"class":3430},[3416,7201,3676],{"class":3422},[3416,7203,4355],{"class":3430},[3416,7205,3676],{"class":3422},[3416,7207,7188],{"class":3430},[3416,7209,7210,7213,7215,7217,7219,7221,7223,7225],{"class":3418,"line":4485},[3416,7211,7212],{"class":3447},"        max_val",[3416,7214,3457],{"class":3430},[3416,7216,3627],{"class":3426},[3416,7218,6931],{"class":3430},[3416,7220,3676],{"class":3422},[3416,7222,4355],{"class":3430},[3416,7224,3676],{"class":3422},[3416,7226,7188],{"class":3430},[3416,7228,7229],{"class":3418,"line":4490},[3416,7230,7231],{"class":3430},"        *,\n",[3416,7233,7234,7237,7239,7242,7244,7247],{"class":3418,"line":4508},[3416,7235,7236],{"class":3447},"        inclusive",[3416,7238,3457],{"class":3430},[3416,7240,7241],{"class":3426},"bool",[3416,7243,4355],{"class":3430},[3416,7245,7246],{"class":3422},"True",[3416,7248,7188],{"class":3430},[3416,7250,7251],{"class":3418,"line":4541},[3416,7252,7253],{"class":3430},"    ):\n",[3416,7255,7256,7258],{"class":3418,"line":4546},[3416,7257,3469],{"class":3422},[3416,7259,7260],{"class":3430},".min_val   = min_val\n",[3416,7262,7263,7265],{"class":3418,"line":4551},[3416,7264,3469],{"class":3422},[3416,7266,7267],{"class":3430},".max_val   = max_val\n",[3416,7269,7270,7272],{"class":3418,"line":4567},[3416,7271,3469],{"class":3422},[3416,7273,7274],{"class":3430},".inclusive = inclusive\n",[3416,7276,7277],{"class":3418,"line":4576},[3416,7278,3491],{"emptyLinePlaceholder":3490},[3416,7280,7281,7283,7285,7287,7289,7291,7293,7295,7297],{"class":3418,"line":4608},[3416,7282,3437],{"class":3422},[3416,7284,6805],{"class":3440},[3416,7286,3444],{"class":3430},[3416,7288,3448],{"class":3447},[3416,7290,3451],{"class":3430},[3416,7292,3667],{"class":3447},[3416,7294,3624],{"class":3430},[3416,7296,3676],{"class":3422},[3416,7298,3431],{"class":3430},[3416,7300,7301,7303,7305,7307,7309,7311,7313,7315],{"class":3418,"line":4613},[3416,7302,3684],{"class":3634},[3416,7304,3687],{"class":3422},[3416,7306,3690],{"class":3440},[3416,7308,3693],{"class":3430},[3416,7310,3696],{"class":3426},[3416,7312,3451],{"class":3430},[3416,7314,3627],{"class":3426},[3416,7316,3703],{"class":3430},[3416,7318,7319,7321,7323],{"class":3418,"line":4619},[3416,7320,3709],{"class":3634},[3416,7322,3712],{"class":3426},[3416,7324,7058],{"class":3430},[3416,7326,7327,7329,7331,7333,7335,7337],{"class":3418,"line":5772},[3416,7328,7063],{"class":3422},[3416,7330,7066],{"class":3500},[3416,7332,4264],{"class":3422},[3416,7334,4267],{"class":3430},[3416,7336,3735],{"class":3422},[3416,7338,7339],{"class":3500},"' має бути числом, \"\n",[3416,7341,7342,7344,7346,7348,7350,7352,7354,7356],{"class":3418,"line":5781},[3416,7343,7063],{"class":3422},[3416,7345,7092],{"class":3500},[3416,7347,3723],{"class":3422},[3416,7349,3726],{"class":3426},[3416,7351,3729],{"class":3430},[3416,7353,3732],{"class":3447},[3416,7355,4279],{"class":3422},[3416,7357,6676],{"class":3500},[3416,7359,7360],{"class":3418,"line":5806},[3416,7361,7049],{"class":3430},[3416,7363,7364,7367,7369,7372,7374,7376,7378],{"class":3418,"line":5811},[3416,7365,7366],{"class":3430},"        too_low  = (",[3416,7368,3448],{"class":3422},[3416,7370,7371],{"class":3430},".min_val ",[3416,7373,4198],{"class":3422},[3416,7375,3687],{"class":3422},[3416,7377,4201],{"class":3422},[3416,7379,7380],{"class":3422}," and\n",[3416,7382,7384,7387,7389,7391,7394,7396,7399,7402,7405,7407],{"class":3418,"line":7383},54,[3416,7385,7386],{"class":3430},"                    (value \u003C ",[3416,7388,3448],{"class":3422},[3416,7390,7371],{"class":3430},[3416,7392,7393],{"class":3634},"if",[3416,7395,3638],{"class":3422},[3416,7397,7398],{"class":3430},".inclusive ",[3416,7400,7401],{"class":3634},"else",[3416,7403,7404],{"class":3430}," value \u003C= ",[3416,7406,3448],{"class":3422},[3416,7408,7409],{"class":3430},".min_val))\n",[3416,7411,7413,7416,7418,7421,7423,7425,7427],{"class":3418,"line":7412},55,[3416,7414,7415],{"class":3430},"        too_high = (",[3416,7417,3448],{"class":3422},[3416,7419,7420],{"class":3430},".max_val ",[3416,7422,4198],{"class":3422},[3416,7424,3687],{"class":3422},[3416,7426,4201],{"class":3422},[3416,7428,7380],{"class":3422},[3416,7430,7432,7435,7437,7439,7441,7443,7445,7447,7450,7452],{"class":3418,"line":7431},56,[3416,7433,7434],{"class":3430},"                    (value > ",[3416,7436,3448],{"class":3422},[3416,7438,7420],{"class":3430},[3416,7440,7393],{"class":3634},[3416,7442,3638],{"class":3422},[3416,7444,7398],{"class":3430},[3416,7446,7401],{"class":3634},[3416,7448,7449],{"class":3430}," value >= ",[3416,7451,3448],{"class":3422},[3416,7453,7454],{"class":3430},".max_val))\n",[3416,7456,7458,7460,7463,7466],{"class":3418,"line":7457},57,[3416,7459,3684],{"class":3634},[3416,7461,7462],{"class":3430}," too_low ",[3416,7464,7465],{"class":3422},"or",[3416,7467,7468],{"class":3430}," too_high:\n",[3416,7470,7472,7475,7477,7480,7482,7485,7487,7489,7491,7494,7496,7499,7502,7504,7506,7508,7510,7513,7515,7517,7519,7521,7523,7525,7527],{"class":3418,"line":7471},58,[3416,7473,7474],{"class":3430},"            bound = ",[3416,7476,3717],{"class":3422},[3416,7478,7479],{"class":3500},"\"[",[3416,7481,4264],{"class":3422},[3416,7483,7484],{"class":3430},".min_val",[3416,7486,3735],{"class":3422},[3416,7488,3451],{"class":3500},[3416,7490,4264],{"class":3422},[3416,7492,7493],{"class":3430},".max_val",[3416,7495,3735],{"class":3422},[3416,7497,7498],{"class":3500},"]\"",[3416,7500,7501],{"class":3634}," if",[3416,7503,3638],{"class":3422},[3416,7505,7398],{"class":3430},[3416,7507,7401],{"class":3634},[3416,7509,4513],{"class":3422},[3416,7511,7512],{"class":3500},"\"(",[3416,7514,4264],{"class":3422},[3416,7516,7484],{"class":3430},[3416,7518,3735],{"class":3422},[3416,7520,3451],{"class":3500},[3416,7522,4264],{"class":3422},[3416,7524,7493],{"class":3430},[3416,7526,3735],{"class":3422},[3416,7528,7529],{"class":3500},")\"\n",[3416,7531,7533,7535,7537],{"class":3418,"line":7532},59,[3416,7534,3709],{"class":3634},[3416,7536,3761],{"class":3426},[3416,7538,7058],{"class":3430},[3416,7540,7542,7544,7546,7548,7550,7552,7555,7557,7559,7561,7564,7566,7569,7571],{"class":3418,"line":7541},60,[3416,7543,7063],{"class":3422},[3416,7545,7066],{"class":3500},[3416,7547,4264],{"class":3422},[3416,7549,4267],{"class":3430},[3416,7551,3735],{"class":3422},[3416,7553,7554],{"class":3500},"' = ",[3416,7556,3723],{"class":3422},[3416,7558,3667],{"class":3430},[3416,7560,3735],{"class":3422},[3416,7562,7563],{"class":3500}," виходить за межі ",[3416,7565,3723],{"class":3422},[3416,7567,7568],{"class":3430},"bound",[3416,7570,3735],{"class":3422},[3416,7572,6676],{"class":3500},[3416,7574,7576],{"class":3418,"line":7575},61,[3416,7577,7049],{"class":3430},[3416,7579,7581],{"class":3418,"line":7580},62,[3416,7582,3491],{"emptyLinePlaceholder":3490},[3416,7584,7586],{"class":3418,"line":7585},63,[3416,7587,3491],{"emptyLinePlaceholder":3490},[3416,7589,7591,7593,7596,7598,7600],{"class":3418,"line":7590},64,[3416,7592,3423],{"class":3422},[3416,7594,7595],{"class":3426}," NonEmptyString",[3416,7597,3444],{"class":3430},[3416,7599,6875],{"class":3426},[3416,7601,3463],{"class":3430},[3416,7603,7605],{"class":3418,"line":7604},65,[3416,7606,4046],{"class":3500},[3416,7608,7610],{"class":3418,"line":7609},66,[3416,7611,7612],{"class":3500},"    Дескриптор: гарантує, що рядок непустий після strip().\n",[3416,7614,7616],{"class":3418,"line":7615},67,[3416,7617,7618],{"class":3500},"    Опціонально обмежує максимальну довжину.\n",[3416,7620,7622],{"class":3418,"line":7621},68,[3416,7623,6891],{"class":3500},[3416,7625,7627],{"class":3418,"line":7626},69,[3416,7628,7629],{"class":3500},"    title       = NonEmptyString()\n",[3416,7631,7633],{"class":3418,"line":7632},70,[3416,7634,7635],{"class":3500},"    description = NonEmptyString(max_length=500)\n",[3416,7637,7639],{"class":3418,"line":7638},71,[3416,7640,4046],{"class":3500},[3416,7642,7644],{"class":3418,"line":7643},72,[3416,7645,3491],{"emptyLinePlaceholder":3490},[3416,7647,7649,7651,7653,7655,7657,7659,7662,7664,7666,7668,7670,7672,7674],{"class":3418,"line":7648},73,[3416,7650,3437],{"class":3422},[3416,7652,3441],{"class":3440},[3416,7654,3444],{"class":3430},[3416,7656,3448],{"class":3447},[3416,7658,3451],{"class":3430},[3416,7660,7661],{"class":3447},"max_length",[3416,7663,3457],{"class":3430},[3416,7665,3696],{"class":3426},[3416,7667,6931],{"class":3430},[3416,7669,3676],{"class":3422},[3416,7671,4355],{"class":3430},[3416,7673,3676],{"class":3422},[3416,7675,3463],{"class":3430},[3416,7677,7679,7681],{"class":3418,"line":7678},74,[3416,7680,3469],{"class":3422},[3416,7682,7683],{"class":3430},".max_length = max_length\n",[3416,7685,7687],{"class":3418,"line":7686},75,[3416,7688,3491],{"emptyLinePlaceholder":3490},[3416,7690,7692,7694,7696,7698,7700,7702,7704,7706,7708],{"class":3418,"line":7691},76,[3416,7693,3437],{"class":3422},[3416,7695,6805],{"class":3440},[3416,7697,3444],{"class":3430},[3416,7699,3448],{"class":3447},[3416,7701,3451],{"class":3430},[3416,7703,3667],{"class":3447},[3416,7705,3624],{"class":3430},[3416,7707,3676],{"class":3422},[3416,7709,3431],{"class":3430},[3416,7711,7713,7715,7717,7719,7721,7723],{"class":3418,"line":7712},77,[3416,7714,3684],{"class":3634},[3416,7716,3687],{"class":3422},[3416,7718,3690],{"class":3440},[3416,7720,6978],{"class":3430},[3416,7722,3460],{"class":3426},[3416,7724,3463],{"class":3430},[3416,7726,7728,7730,7732],{"class":3418,"line":7727},78,[3416,7729,3709],{"class":3634},[3416,7731,3712],{"class":3426},[3416,7733,7058],{"class":3430},[3416,7735,7737,7739,7741,7743,7745,7747],{"class":3418,"line":7736},79,[3416,7738,7063],{"class":3422},[3416,7740,7066],{"class":3500},[3416,7742,4264],{"class":3422},[3416,7744,4267],{"class":3430},[3416,7746,3735],{"class":3422},[3416,7748,7749],{"class":3500},"' має бути рядком, \"\n",[3416,7751,7753,7755,7757,7759,7761,7763,7765,7767],{"class":3418,"line":7752},80,[3416,7754,7063],{"class":3422},[3416,7756,7092],{"class":3500},[3416,7758,3723],{"class":3422},[3416,7760,3726],{"class":3426},[3416,7762,3729],{"class":3430},[3416,7764,3732],{"class":3447},[3416,7766,4279],{"class":3422},[3416,7768,6676],{"class":3500},[3416,7770,7772],{"class":3418,"line":7771},81,[3416,7773,7049],{"class":3430},[3416,7775,7777],{"class":3418,"line":7776},82,[3416,7778,7779],{"class":3430},"        stripped = value.strip()\n",[3416,7781,7783,7785,7787],{"class":3418,"line":7782},83,[3416,7784,3684],{"class":3634},[3416,7786,3687],{"class":3422},[3416,7788,7789],{"class":3430}," stripped:\n",[3416,7791,7793,7795,7797],{"class":3418,"line":7792},84,[3416,7794,3709],{"class":3634},[3416,7796,3761],{"class":3426},[3416,7798,7058],{"class":3430},[3416,7800,7802,7804,7806,7808,7810,7812],{"class":3418,"line":7801},85,[3416,7803,7063],{"class":3422},[3416,7805,7066],{"class":3500},[3416,7807,4264],{"class":3422},[3416,7809,4267],{"class":3430},[3416,7811,3735],{"class":3422},[3416,7813,7814],{"class":3500},"' не може бути порожнім рядком\"\n",[3416,7816,7818],{"class":3418,"line":7817},86,[3416,7819,7049],{"class":3430},[3416,7821,7823,7825,7827,7830,7832,7834,7836,7839,7842,7845,7847],{"class":3418,"line":7822},87,[3416,7824,3684],{"class":3634},[3416,7826,3638],{"class":3422},[3416,7828,7829],{"class":3430},".max_length ",[3416,7831,4198],{"class":3422},[3416,7833,3687],{"class":3422},[3416,7835,4201],{"class":3422},[3416,7837,7838],{"class":3422}," and",[3416,7840,7841],{"class":3440}," len",[3416,7843,7844],{"class":3430},"(stripped) > ",[3416,7846,3448],{"class":3422},[3416,7848,7849],{"class":3430},".max_length:\n",[3416,7851,7853,7855,7857],{"class":3418,"line":7852},88,[3416,7854,3709],{"class":3634},[3416,7856,3761],{"class":3426},[3416,7858,7058],{"class":3430},[3416,7860,7862,7864,7866,7868,7870,7872],{"class":3418,"line":7861},89,[3416,7863,7063],{"class":3422},[3416,7865,7066],{"class":3500},[3416,7867,4264],{"class":3422},[3416,7869,4267],{"class":3430},[3416,7871,3735],{"class":3422},[3416,7873,7874],{"class":3500},"' перевищує максимальну довжину \"\n",[3416,7876,7878,7880,7882,7884,7887,7889,7892,7894,7897,7900,7902],{"class":3418,"line":7877},90,[3416,7879,7063],{"class":3422},[3416,7881,3738],{"class":3500},[3416,7883,4264],{"class":3422},[3416,7885,7886],{"class":3430},".max_length",[3416,7888,3735],{"class":3422},[3416,7890,7891],{"class":3500}," символів (фактично: ",[3416,7893,3723],{"class":3422},[3416,7895,7896],{"class":3440},"len",[3416,7898,7899],{"class":3430},"(stripped)",[3416,7901,3735],{"class":3422},[3416,7903,7529],{"class":3500},[3416,7905,7907],{"class":3418,"line":7906},91,[3416,7908,7049],{"class":3430},[3850,7910,7912],{"id":7911},"застосування-валідаторів-у-доменних-класах","Застосування валідаторів у доменних класах",[3394,7914,7915],{},"Тепер подивимось, як ці валідатори застосовуються у реальних класах — і наскільки декларативним стає код:",[3407,7917,7919],{"className":3409,"code":7918,"language":3411,"meta":3412,"style":3412},"# domain_classes.py\nfrom validators import TypedField, RangedField, NonEmptyString\n\n\nclass PhysicalSensor:\n    \"\"\"\n    Датчик фізичних вимірювань.\n    Всі поля з автоматичною валідацією — жодного @property, жодного дублювання.\n    \"\"\"\n    name        = NonEmptyString(max_length=100)\n    temperature = RangedField(-273.15, 1_000_000.0)   # в Цельсіях\n    humidity    = RangedField(0.0, 100.0)              # у відсотках\n    pressure    = RangedField(0.0, None)               # тиск ≥ 0, верхньої межі немає\n\n    def __init__(\n        self,\n        name: str,\n        temperature: float,\n        humidity: float,\n        pressure: float,\n    ) -> None:\n        self.name        = name\n        self.temperature = temperature\n        self.humidity    = humidity\n        self.pressure    = pressure\n\n    def __repr__(self) -> str:\n        return (\n            f\"PhysicalSensor({self.name!r}, \"\n            f\"T={self.temperature}°C, \"\n            f\"H={self.humidity}%, \"\n            f\"P={self.pressure} hPa)\"\n        )\n\n\nclass Product:\n    \"\"\"Товар в інтернет-магазині.\"\"\"\n    title    = NonEmptyString(max_length=200)\n    price    = RangedField(0.01, None)                 # ціна > 0\n    quantity = RangedField(0, None)                    # кількість ≥ 0\n    sku      = NonEmptyString()\n\n    def __init__(self, title: str, price: float, quantity: int, sku: str) -> None:\n        self.title    = title\n        self.price    = price\n        self.quantity = quantity\n        self.sku      = sku\n\n\n# --- Тестування ---\nsensor = PhysicalSensor(\"Кімнатний #1\", temperature=22.5, humidity=55.0, pressure=1013.25)\nprint(sensor)\n\n# Валідні зміни\nsensor.temperature = 25.0\nsensor.humidity    = 60.0\n\n# Невалідні зміни — кожна підіймає виключення\ntry:\n    sensor.temperature = -300  # нижче абсолютного нуля\nexcept ValueError as e:\n    print(f\"ValueError: {e}\")\n\ntry:\n    sensor.humidity = 150      # вологість > 100%\nexcept ValueError as e:\n    print(f\"ValueError: {e}\")\n\ntry:\n    sensor.name = \"\"           # порожній рядок\nexcept ValueError as e:\n    print(f\"ValueError: {e}\")\n\ntry:\n    sensor.pressure = \"висока\"  # рядок замість числа\nexcept TypeError as e:\n    print(f\"TypeError: {e}\")\n",[3398,7920,7921,7926,7938,7942,7946,7955,7959,7964,7969,7973,7986,8003,8022,8039,8043,8051,8057,8068,8079,8090,8101,8110,8117,8124,8131,8138,8142,8158,8165,8181,8198,8215,8232,8237,8241,8245,8254,8259,8273,8291,8308,8313,8317,8369,8376,8383,8390,8397,8401,8405,8410,8448,8455,8459,8464,8472,8480,8484,8489,8496,8507,8520,8543,8547,8553,8564,8574,8594,8598,8604,8615,8625,8645,8649,8655,8666,8676],{"__ignoreMap":3412},[3416,7922,7923],{"class":3418,"line":3419},[3416,7924,7925],{"class":3516},"# domain_classes.py\n",[3416,7927,7928,7930,7933,7935],{"class":3418,"line":3434},[3416,7929,6564],{"class":3634},[3416,7931,7932],{"class":3430}," validators ",[3416,7934,6570],{"class":3634},[3416,7936,7937],{"class":3430}," TypedField, RangedField, NonEmptyString\n",[3416,7939,7940],{"class":3418,"line":3466},[3416,7941,3491],{"emptyLinePlaceholder":3490},[3416,7943,7944],{"class":3418,"line":3475},[3416,7945,3491],{"emptyLinePlaceholder":3490},[3416,7947,7948,7950,7953],{"class":3418,"line":3487},[3416,7949,3423],{"class":3422},[3416,7951,7952],{"class":3426}," PhysicalSensor",[3416,7954,3431],{"class":3430},[3416,7956,7957],{"class":3418,"line":3494},[3416,7958,4046],{"class":3500},[3416,7960,7961],{"class":3418,"line":3507},[3416,7962,7963],{"class":3500},"    Датчик фізичних вимірювань.\n",[3416,7965,7966],{"class":3418,"line":3520},[3416,7967,7968],{"class":3500},"    Всі поля з автоматичною валідацією — жодного @property, жодного дублювання.\n",[3416,7970,7971],{"class":3418,"line":3532},[3416,7972,4046],{"class":3500},[3416,7974,7975,7978,7980,7982,7984],{"class":3418,"line":3648},[3416,7976,7977],{"class":3430},"    name        = NonEmptyString(",[3416,7979,7661],{"class":3447},[3416,7981,4177],{"class":3430},[3416,7983,6484],{"class":3483},[3416,7985,3504],{"class":3430},[3416,7987,7988,7991,7993,7995,7998,8000],{"class":3418,"line":3654},[3416,7989,7990],{"class":3430},"    temperature = RangedField(-",[3416,7992,3751],{"class":3483},[3416,7994,3451],{"class":3430},[3416,7996,7997],{"class":3483},"1_000_000.0",[3416,7999,5656],{"class":3430},[3416,8001,8002],{"class":3516},"# в Цельсіях\n",[3416,8004,8005,8008,8011,8013,8016,8019],{"class":3418,"line":3681},[3416,8006,8007],{"class":3430},"    humidity    = RangedField(",[3416,8009,8010],{"class":3483},"0.0",[3416,8012,3451],{"class":3430},[3416,8014,8015],{"class":3483},"100.0",[3416,8017,8018],{"class":3430},")              ",[3416,8020,8021],{"class":3516},"# у відсотках\n",[3416,8023,8024,8027,8029,8031,8033,8036],{"class":3418,"line":3706},[3416,8025,8026],{"class":3430},"    pressure    = RangedField(",[3416,8028,8010],{"class":3483},[3416,8030,3451],{"class":3430},[3416,8032,3676],{"class":3422},[3416,8034,8035],{"class":3430},")               ",[3416,8037,8038],{"class":3516},"# тиск ≥ 0, верхньої межі немає\n",[3416,8040,8041],{"class":3418,"line":3743},[3416,8042,3491],{"emptyLinePlaceholder":3490},[3416,8044,8045,8047,8049],{"class":3418,"line":3756},[3416,8046,3437],{"class":3422},[3416,8048,3441],{"class":3440},[3416,8050,7058],{"class":3430},[3416,8052,8053,8055],{"class":3418,"line":3782},[3416,8054,3469],{"class":3447},[3416,8056,7188],{"class":3430},[3416,8058,8059,8062,8064,8066],{"class":3418,"line":4157},[3416,8060,8061],{"class":3447},"        name",[3416,8063,3457],{"class":3430},[3416,8065,3460],{"class":3426},[3416,8067,7188],{"class":3430},[3416,8069,8070,8073,8075,8077],{"class":3418,"line":4184},[3416,8071,8072],{"class":3447},"        temperature",[3416,8074,3457],{"class":3430},[3416,8076,3627],{"class":3426},[3416,8078,7188],{"class":3430},[3416,8080,8081,8084,8086,8088],{"class":3418,"line":4190},[3416,8082,8083],{"class":3447},"        humidity",[3416,8085,3457],{"class":3430},[3416,8087,3627],{"class":3426},[3416,8089,7188],{"class":3430},[3416,8091,8092,8095,8097,8099],{"class":3418,"line":4206},[3416,8093,8094],{"class":3447},"        pressure",[3416,8096,3457],{"class":3430},[3416,8098,3627],{"class":3426},[3416,8100,7188],{"class":3430},[3416,8102,8103,8106,8108],{"class":3418,"line":4217},[3416,8104,8105],{"class":3430},"    ) -> ",[3416,8107,3676],{"class":3422},[3416,8109,3431],{"class":3430},[3416,8111,8112,8114],{"class":3418,"line":4238},[3416,8113,3469],{"class":3422},[3416,8115,8116],{"class":3430},".name        = name\n",[3416,8118,8119,8121],{"class":3418,"line":4286},[3416,8120,3469],{"class":3422},[3416,8122,8123],{"class":3430},".temperature = temperature\n",[3416,8125,8126,8128],{"class":3418,"line":4294},[3416,8127,3469],{"class":3422},[3416,8129,8130],{"class":3430},".humidity    = humidity\n",[3416,8132,8133,8135],{"class":3418,"line":4299},[3416,8134,3469],{"class":3422},[3416,8136,8137],{"class":3430},".pressure    = pressure\n",[3416,8139,8140],{"class":3418,"line":4325},[3416,8141,3491],{"emptyLinePlaceholder":3490},[3416,8143,8144,8146,8148,8150,8152,8154,8156],{"class":3418,"line":4368},[3416,8145,3437],{"class":3422},[3416,8147,4495],{"class":3440},[3416,8149,3444],{"class":3430},[3416,8151,3448],{"class":3447},[3416,8153,3624],{"class":3430},[3416,8155,3460],{"class":3426},[3416,8157,3431],{"class":3430},[3416,8159,8160,8162],{"class":3418,"line":4381},[3416,8161,3635],{"class":3634},[3416,8163,8164],{"class":3430}," (\n",[3416,8166,8167,8170,8173,8175,8177,8179],{"class":3418,"line":4386},[3416,8168,8169],{"class":3422},"            f",[3416,8171,8172],{"class":3500},"\"PhysicalSensor(",[3416,8174,4264],{"class":3422},[3416,8176,4521],{"class":3430},[3416,8178,4279],{"class":3422},[3416,8180,7085],{"class":3500},[3416,8182,8183,8185,8188,8190,8193,8195],{"class":3418,"line":4391},[3416,8184,8169],{"class":3422},[3416,8186,8187],{"class":3500},"\"T=",[3416,8189,4264],{"class":3422},[3416,8191,8192],{"class":3430},".temperature",[3416,8194,3735],{"class":3422},[3416,8196,8197],{"class":3500},"°C, \"\n",[3416,8199,8200,8202,8205,8207,8210,8212],{"class":3418,"line":4401},[3416,8201,8169],{"class":3422},[3416,8203,8204],{"class":3500},"\"H=",[3416,8206,4264],{"class":3422},[3416,8208,8209],{"class":3430},".humidity",[3416,8211,3735],{"class":3422},[3416,8213,8214],{"class":3500},"%, \"\n",[3416,8216,8217,8219,8222,8224,8227,8229],{"class":3418,"line":4407},[3416,8218,8169],{"class":3422},[3416,8220,8221],{"class":3500},"\"P=",[3416,8223,4264],{"class":3422},[3416,8225,8226],{"class":3430},".pressure",[3416,8228,3735],{"class":3422},[3416,8230,8231],{"class":3500}," hPa)\"\n",[3416,8233,8234],{"class":3418,"line":4413},[3416,8235,8236],{"class":3430},"        )\n",[3416,8238,8239],{"class":3418,"line":4419},[3416,8240,3491],{"emptyLinePlaceholder":3490},[3416,8242,8243],{"class":3418,"line":4425},[3416,8244,3491],{"emptyLinePlaceholder":3490},[3416,8246,8247,8249,8252],{"class":3418,"line":4430},[3416,8248,3423],{"class":3422},[3416,8250,8251],{"class":3426}," Product",[3416,8253,3431],{"class":3430},[3416,8255,8256],{"class":3418,"line":4464},[3416,8257,8258],{"class":3500},"    \"\"\"Товар в інтернет-магазині.\"\"\"\n",[3416,8260,8261,8264,8266,8268,8271],{"class":3418,"line":4475},[3416,8262,8263],{"class":3430},"    title    = NonEmptyString(",[3416,8265,7661],{"class":3447},[3416,8267,4177],{"class":3430},[3416,8269,8270],{"class":3483},"200",[3416,8272,3504],{"class":3430},[3416,8274,8275,8278,8281,8283,8285,8288],{"class":3418,"line":4485},[3416,8276,8277],{"class":3430},"    price    = RangedField(",[3416,8279,8280],{"class":3483},"0.01",[3416,8282,3451],{"class":3430},[3416,8284,3676],{"class":3422},[3416,8286,8287],{"class":3430},")                 ",[3416,8289,8290],{"class":3516},"# ціна > 0\n",[3416,8292,8293,8296,8298,8300,8302,8305],{"class":3418,"line":4490},[3416,8294,8295],{"class":3430},"    quantity = RangedField(",[3416,8297,6417],{"class":3483},[3416,8299,3451],{"class":3430},[3416,8301,3676],{"class":3422},[3416,8303,8304],{"class":3430},")                    ",[3416,8306,8307],{"class":3516},"# кількість ≥ 0\n",[3416,8309,8310],{"class":3418,"line":4508},[3416,8311,8312],{"class":3430},"    sku      = NonEmptyString()\n",[3416,8314,8315],{"class":3418,"line":4541},[3416,8316,3491],{"emptyLinePlaceholder":3490},[3416,8318,8319,8321,8323,8325,8327,8329,8332,8334,8336,8338,8341,8343,8345,8347,8350,8352,8354,8356,8359,8361,8363,8365,8367],{"class":3418,"line":4546},[3416,8320,3437],{"class":3422},[3416,8322,3441],{"class":3440},[3416,8324,3444],{"class":3430},[3416,8326,3448],{"class":3447},[3416,8328,3451],{"class":3430},[3416,8330,8331],{"class":3447},"title",[3416,8333,3457],{"class":3430},[3416,8335,3460],{"class":3426},[3416,8337,3451],{"class":3430},[3416,8339,8340],{"class":3447},"price",[3416,8342,3457],{"class":3430},[3416,8344,3627],{"class":3426},[3416,8346,3451],{"class":3430},[3416,8348,8349],{"class":3447},"quantity",[3416,8351,3457],{"class":3430},[3416,8353,3696],{"class":3426},[3416,8355,3451],{"class":3430},[3416,8357,8358],{"class":3447},"sku",[3416,8360,3457],{"class":3430},[3416,8362,3460],{"class":3426},[3416,8364,3624],{"class":3430},[3416,8366,3676],{"class":3422},[3416,8368,3431],{"class":3430},[3416,8370,8371,8373],{"class":3418,"line":4551},[3416,8372,3469],{"class":3422},[3416,8374,8375],{"class":3430},".title    = title\n",[3416,8377,8378,8380],{"class":3418,"line":4567},[3416,8379,3469],{"class":3422},[3416,8381,8382],{"class":3430},".price    = price\n",[3416,8384,8385,8387],{"class":3418,"line":4576},[3416,8386,3469],{"class":3422},[3416,8388,8389],{"class":3430},".quantity = quantity\n",[3416,8391,8392,8394],{"class":3418,"line":4608},[3416,8393,3469],{"class":3422},[3416,8395,8396],{"class":3430},".sku      = sku\n",[3416,8398,8399],{"class":3418,"line":4613},[3416,8400,3491],{"emptyLinePlaceholder":3490},[3416,8402,8403],{"class":3418,"line":4619},[3416,8404,3491],{"emptyLinePlaceholder":3490},[3416,8406,8407],{"class":3418,"line":5772},[3416,8408,8409],{"class":3516},"# --- Тестування ---\n",[3416,8411,8412,8415,8418,8420,8422,8424,8426,8428,8431,8433,8436,8438,8441,8443,8446],{"class":3418,"line":5781},[3416,8413,8414],{"class":3430},"sensor = PhysicalSensor(",[3416,8416,8417],{"class":3500},"\"Кімнатний #1\"",[3416,8419,3451],{"class":3430},[3416,8421,3404],{"class":3447},[3416,8423,4177],{"class":3430},[3416,8425,3513],{"class":3483},[3416,8427,3451],{"class":3430},[3416,8429,8430],{"class":3447},"humidity",[3416,8432,4177],{"class":3430},[3416,8434,8435],{"class":3483},"55.0",[3416,8437,3451],{"class":3430},[3416,8439,8440],{"class":3447},"pressure",[3416,8442,4177],{"class":3430},[3416,8444,8445],{"class":3483},"1013.25",[3416,8447,3504],{"class":3430},[3416,8449,8450,8452],{"class":3418,"line":5806},[3416,8451,4570],{"class":3440},[3416,8453,8454],{"class":3430},"(sensor)\n",[3416,8456,8457],{"class":3418,"line":5811},[3416,8458,3491],{"emptyLinePlaceholder":3490},[3416,8460,8461],{"class":3418,"line":7383},[3416,8462,8463],{"class":3516},"# Валідні зміни\n",[3416,8465,8466,8469],{"class":3418,"line":7412},[3416,8467,8468],{"class":3430},"sensor.temperature = ",[3416,8470,8471],{"class":3483},"25.0\n",[3416,8473,8474,8477],{"class":3418,"line":7431},[3416,8475,8476],{"class":3430},"sensor.humidity    = ",[3416,8478,8479],{"class":3483},"60.0\n",[3416,8481,8482],{"class":3418,"line":7457},[3416,8483,3491],{"emptyLinePlaceholder":3490},[3416,8485,8486],{"class":3418,"line":7471},[3416,8487,8488],{"class":3516},"# Невалідні зміни — кожна підіймає виключення\n",[3416,8490,8491,8494],{"class":3418,"line":7532},[3416,8492,8493],{"class":3634},"try",[3416,8495,3431],{"class":3430},[3416,8497,8498,8501,8504],{"class":3418,"line":7541},[3416,8499,8500],{"class":3430},"    sensor.temperature = -",[3416,8502,8503],{"class":3483},"300",[3416,8505,8506],{"class":3516},"  # нижче абсолютного нуля\n",[3416,8508,8509,8512,8514,8517],{"class":3418,"line":7575},[3416,8510,8511],{"class":3634},"except",[3416,8513,3761],{"class":3426},[3416,8515,8516],{"class":3634}," as",[3416,8518,8519],{"class":3430}," e:\n",[3416,8521,8522,8525,8527,8529,8532,8534,8537,8539,8541],{"class":3418,"line":7580},[3416,8523,8524],{"class":3440},"    print",[3416,8526,3444],{"class":3430},[3416,8528,3717],{"class":3422},[3416,8530,8531],{"class":3500},"\"ValueError: ",[3416,8533,3723],{"class":3422},[3416,8535,8536],{"class":3430},"e",[3416,8538,3735],{"class":3422},[3416,8540,3738],{"class":3500},[3416,8542,3504],{"class":3430},[3416,8544,8545],{"class":3418,"line":7585},[3416,8546,3491],{"emptyLinePlaceholder":3490},[3416,8548,8549,8551],{"class":3418,"line":7590},[3416,8550,8493],{"class":3634},[3416,8552,3431],{"class":3430},[3416,8554,8555,8558,8561],{"class":3418,"line":7604},[3416,8556,8557],{"class":3430},"    sensor.humidity = ",[3416,8559,8560],{"class":3483},"150",[3416,8562,8563],{"class":3516},"      # вологість > 100%\n",[3416,8565,8566,8568,8570,8572],{"class":3418,"line":7609},[3416,8567,8511],{"class":3634},[3416,8569,3761],{"class":3426},[3416,8571,8516],{"class":3634},[3416,8573,8519],{"class":3430},[3416,8575,8576,8578,8580,8582,8584,8586,8588,8590,8592],{"class":3418,"line":7615},[3416,8577,8524],{"class":3440},[3416,8579,3444],{"class":3430},[3416,8581,3717],{"class":3422},[3416,8583,8531],{"class":3500},[3416,8585,3723],{"class":3422},[3416,8587,8536],{"class":3430},[3416,8589,3735],{"class":3422},[3416,8591,3738],{"class":3500},[3416,8593,3504],{"class":3430},[3416,8595,8596],{"class":3418,"line":7621},[3416,8597,3491],{"emptyLinePlaceholder":3490},[3416,8599,8600,8602],{"class":3418,"line":7626},[3416,8601,8493],{"class":3634},[3416,8603,3431],{"class":3430},[3416,8605,8606,8609,8612],{"class":3418,"line":7632},[3416,8607,8608],{"class":3430},"    sensor.name = ",[3416,8610,8611],{"class":3500},"\"\"",[3416,8613,8614],{"class":3516},"           # порожній рядок\n",[3416,8616,8617,8619,8621,8623],{"class":3418,"line":7638},[3416,8618,8511],{"class":3634},[3416,8620,3761],{"class":3426},[3416,8622,8516],{"class":3634},[3416,8624,8519],{"class":3430},[3416,8626,8627,8629,8631,8633,8635,8637,8639,8641,8643],{"class":3418,"line":7643},[3416,8628,8524],{"class":3440},[3416,8630,3444],{"class":3430},[3416,8632,3717],{"class":3422},[3416,8634,8531],{"class":3500},[3416,8636,3723],{"class":3422},[3416,8638,8536],{"class":3430},[3416,8640,3735],{"class":3422},[3416,8642,3738],{"class":3500},[3416,8644,3504],{"class":3430},[3416,8646,8647],{"class":3418,"line":7648},[3416,8648,3491],{"emptyLinePlaceholder":3490},[3416,8650,8651,8653],{"class":3418,"line":7678},[3416,8652,8493],{"class":3634},[3416,8654,3431],{"class":3430},[3416,8656,8657,8660,8663],{"class":3418,"line":7686},[3416,8658,8659],{"class":3430},"    sensor.pressure = ",[3416,8661,8662],{"class":3500},"\"висока\"",[3416,8664,8665],{"class":3516},"  # рядок замість числа\n",[3416,8667,8668,8670,8672,8674],{"class":3418,"line":7691},[3416,8669,8511],{"class":3634},[3416,8671,3712],{"class":3426},[3416,8673,8516],{"class":3634},[3416,8675,8519],{"class":3430},[3416,8677,8678,8680,8682,8684,8687,8689,8691,8693,8695],{"class":3418,"line":7712},[3416,8679,8524],{"class":3440},[3416,8681,3444],{"class":3430},[3416,8683,3717],{"class":3422},[3416,8685,8686],{"class":3500},"\"TypeError: ",[3416,8688,3723],{"class":3422},[3416,8690,8536],{"class":3430},[3416,8692,3735],{"class":3422},[3416,8694,3738],{"class":3500},[3416,8696,3504],{"class":3430},[4645,8698,8700,8708,8724,8733,8740,8747],{"title":8699},"python domain_classes.py",[4649,8701,8703,4657,8706],{"className":8702},[3418],[3416,8704,4656],{"className":8705},[4655],[3838,8707,8699],{},[4649,8709,8711,8712,8715,8716,8719,8720,8723],{"className":8710},[3418],"PhysicalSensor('Кімнатний #1', T=",[3416,8713,3513],{"className":8714},[4683],"°C, H=",[3416,8717,8435],{"className":8718},[4683],"%, P=",[3416,8721,8445],{"className":8722},[4683]," hPa)",[4649,8725,8727,8728],{"className":8726},[3418],"ValueError: ",[3416,8729,8732],{"className":8730},[8731],"text-rose-400","Атрибут 'temperature' = -300 виходить за межі [-273.15, 1000000.0]",[4649,8734,8727,8736],{"className":8735},[3418],[3416,8737,8739],{"className":8738},[8731],"Атрибут 'humidity' = 150 виходить за межі [0.0, 100.0]",[4649,8741,8727,8743],{"className":8742},[3418],[3416,8744,8746],{"className":8745},[8731],"Атрибут 'name' не може бути порожнім рядком",[4649,8748,8750,8751],{"className":8749},[3418],"TypeError: ",[3416,8752,8754],{"className":8753},[8731],"Атрибут 'pressure' має бути числом, отримано 'str'",[3850,8756,8758],{"id":8757},"ключові-переваги-підходу","Ключові переваги підходу",[3394,8760,8761,8762,8765],{},"Порівняємо кількість коду. У класі ",[3398,8763,8764],{},"PhysicalSensor"," з 4 валідованими полями:",[3896,8767,8768,8776],{},[3899,8769,8770,8775],{},[3838,8771,8772,8773,3550],{},"З ",[3398,8774,3549],{}," ~40 рядків геттерів\u002Fсеттерів + дублювання в кожному новому класі",[3899,8777,8778,8781,8782,8785,8786],{},[3838,8779,8780],{},"З дескрипторами:"," 4 рядки декларацій (",[3398,8783,8784],{},"name = NonEmptyString(...)"," тощо) + один раз написані класи у ",[3398,8787,8788],{},"validators.py",[3394,8790,8791,8792,8795,8796,8799,8800,8802],{},"Але ще важливіша ",[3838,8793,8794],{},"семантична виразність",": рядок ",[3398,8797,8798],{},"temperature = RangedField(-273.15, 1_000_000.0)"," читається як документація. Він відразу каже: «temperature — це числове поле в діапазоні від абсолютного нуля до мільйона». Ніякого ",[3398,8801,3549],{}," з умовами всередині не потрібно читати.",[6189,8804,8805,8806,3451,8809,8812,8813,8816],{},"Такий підхід — основа того, як влаштовані ORM-поля у Django (",[3398,8807,8808],{},"models.CharField(max_length=200)",[3398,8810,8811],{},"models.FloatField()",") і поля у Pydantic (",[3398,8814,8815],{},"Field(ge=0, le=100)","). Ці бібліотеки реалізують значно складніші версії саме цього патерну.",[3843,8818],{},[3389,8820,8822],{"id":8821},"частина-iv-практичний-сценарій-2-ледаче-обчислення-lazy-cached-property","Частина IV: Практичний сценарій 2 — Ледаче обчислення (Lazy \u002F Cached Property)",[3850,8824,8826],{"id":8825},"проблема-дорогих-обчислень","Проблема дорогих обчислень",[3394,8828,8829,8830,8833],{},"Уявіть клас, що представляє документ із текстом. Є кілька «похідних» атрибутів, які обчислюються на основі тексту: кількість слів, індекс читабельності, граматичний розбір, хеш для пошуку дублікатів. Ці обчислення — ",[3838,8831,8832],{},"дорогі",": займають десятки мілісекунд і потребують зовнішніх бібліотек.",[3394,8835,8836,8837,8839],{},"Наївний підхід — обчислювати все в ",[3398,8838,4905],{},". Але це катастрофа: завантажили 10 000 документів — заплатили повну вартість обчислень навіть для тих, до яких ніхто ніколи не зверниться.",[3394,8841,8842,8843,8846],{},"Правильне рішення — ",[3838,8844,8845],{},"ледаче обчислення (lazy evaluation)",": атрибут обчислюється лише при першому зверненні та кешується для подальших. Це класичний патерн «обчисли один раз — зберігай назавжди».",[3809,8848,8849,8854,8859],{},[3812,8850,8853],{"icon":8851,"title":8852},"i-heroicons-arrow-path","Звичайний @property","Обчислює значення при кожному зверненні. Гарно, але дороговартісно якщо операція не безкоштовна — наприклад, розбір тексту або запит до БД.",[3812,8855,8858],{"icon":8856,"title":8857},"i-heroicons-bolt","Обчислення у __init__","Розраховує все наперед. Дуже швидкий доступ, але повна вартість сплачується одразу — навіть якщо атрибут не знадобиться ніколи.",[3812,8860,8863,8864,8866],{"icon":8861,"title":8862},"i-heroicons-cpu-chip","Lazy \u002F Cached Property","Обчислює при першому зверненні, кешує результат у ",[3398,8865,4758],{}," екземпляра. Подальші звернення — миттєві. Ідеальний баланс між першими двома.",[3850,8868,8870],{"id":8869},"реалізація-через-non-data-descriptor","Реалізація через non-data descriptor",[3394,8872,8873,8874,8877,8878,8880,8881,8883,8884,8886,8887,8889],{},"Ключ до реалізації ",[3398,8875,8876],{},"cached_property"," — використати ",[3838,8879,4919],{}," навмисно. Пригадаємо алгоритм пошуку: non-data descriptor (є ",[3398,8882,3862],{},", немає ",[3398,8885,3865],{},") має нижчий пріоритет, ніж ",[3398,8888,4758],{}," екземпляра. Це саме те, що нам потрібно:",[8891,8892,8893,8908],"ol",{},[3899,8894,8895,8896,8898,8899,8901,8902,8907],{},"При першому зверненні: ",[3398,8897,4894],{}," порожній за цим ключем → викликається ",[3398,8900,3862],{}," → обчислюємо значення → ",[3838,8903,8904,8905],{},"зберігаємо у ",[3398,8906,4894],{}," за тим самим іменем",[3899,8909,8910,8911,4657,8913,8916,8917,8919],{},"При наступних зверненнях: Python знаходить значення у ",[3398,8912,4894],{},[3838,8914,8915],{},"раніше",", ніж дійде до дескриптора → ",[3398,8918,3862],{}," більше не викликається",[3394,8921,8922],{},"Таким чином дескриптор сам себе «відключає» після першого спрацювання:",[3407,8924,8926],{"className":3409,"code":8925,"language":3411,"meta":3412,"style":3412},"# lazy_property.py\nimport time\nfrom typing import Callable, TypeVar, Any\n\nT = TypeVar(\"T\")\n\n\nclass lazy_property:\n    \"\"\"\n    Non-data descriptor для ледачого обчислення атрибутів.\n    \n    При першому зверненні до атрибуту викликає обгорнуту функцію,\n    зберігає результат у obj.__dict__ під тим самим ім'ям і повертає його.\n    Усі наступні звернення отримують значення напряму з __dict__,\n    оминаючи дескриптор повністю.\n    \n    Використання:\n        class MyClass:\n            @lazy_property\n            def expensive_value(self) -> int:\n                return heavy_computation()\n    \"\"\"\n\n    def __init__(self, func: Callable) -> None:\n        self.func      = func\n        self.attr_name = None           # заповниться у __set_name__\n        self.__doc__   = func.__doc__   # зберігаємо документацію\n\n    def __set_name__(self, owner: type, name: str) -> None:\n        self.attr_name = name\n\n    def __get__(self, obj, objtype=None) -> Any:\n        if obj is None:\n            return self  # доступ через клас → повертаємо сам дескриптор\n\n        if self.attr_name is None:\n            raise TypeError(\n                \"lazy_property не можна використовувати без __set_name__. \"\n                \"Переконайтесь, що він оголошений як атрибут класу.\"\n            )\n\n        # Обчислюємо значення і одразу записуємо у __dict__ екземпляра.\n        # Наступного разу Python знайде його там і не дійде до нас (non-data!).\n        print(f\"  [lazy_property] Обчислюємо '{self.attr_name}' для {type(obj).__name__}...\")\n        value = self.func(obj)\n        obj.__dict__[self.attr_name] = value\n        return value\n\n    # Немає __set__ → це non-data descriptor → __dict__ екземпляра має пріоритет\n\n\nclass Document:\n    \"\"\"\n    Документ із текстовим вмістом.\n    Всі похідні атрибути обчислюються ледаче — лише при першому зверненні.\n    \"\"\"\n\n    def __init__(self, text: str) -> None:\n        self.text = text\n\n    @lazy_property\n    def word_count(self) -> int:\n        \"\"\"Кількість слів у документі.\"\"\"\n        time.sleep(0.05)  # симуляція важкого NLP-аналізу\n        return len(self.text.split())\n\n    @lazy_property\n    def unique_words(self) -> set[str]:\n        \"\"\"Множина унікальних слів (у нижньому регістрі).\"\"\"\n        time.sleep(0.05)\n        return {w.lower().strip(\".,!?;:\") for w in self.text.split()}\n\n    @lazy_property\n    def char_frequency(self) -> dict[str, int]:\n        \"\"\"Частота кожного символу (без пробілів).\"\"\"\n        time.sleep(0.05)\n        freq: dict[str, int] = {}\n        for ch in self.text.replace(\" \", \"\"):\n            freq[ch] = freq.get(ch, 0) + 1\n        return dict(sorted(freq.items(), key=lambda x: x[1], reverse=True))\n\n    @lazy_property\n    def reading_time_minutes(self) -> float:\n        \"\"\"Приблизний час читання (200 слів\u002Fхв — середня швидкість).\"\"\"\n        return round(self.word_count \u002F 200, 2)\n\n    def __repr__(self) -> str:\n        return f\"Document({len(self.text)} chars)\"\n",[3398,8927,8928,8933,8940,8952,8956,8966,8970,8974,8983,8987,8992,8996,9001,9006,9011,9016,9020,9025,9030,9035,9040,9045,9049,9053,9075,9082,9094,9111,9115,9147,9154,9158,9183,9195,9204,9208,9223,9231,9236,9241,9245,9249,9254,9259,9295,9304,9317,9323,9327,9332,9336,9340,9349,9353,9358,9363,9367,9371,9396,9403,9407,9412,9429,9434,9448,9461,9465,9469,9488,9493,9501,9526,9530,9534,9556,9561,9569,9583,9607,9620,9665,9669,9673,9690,9695,9718,9722,9738],{"__ignoreMap":3412},[3416,8929,8930],{"class":3418,"line":3419},[3416,8931,8932],{"class":3516},"# lazy_property.py\n",[3416,8934,8935,8937],{"class":3418,"line":3434},[3416,8936,6570],{"class":3634},[3416,8938,8939],{"class":3430}," time\n",[3416,8941,8942,8944,8947,8949],{"class":3418,"line":3466},[3416,8943,6564],{"class":3634},[3416,8945,8946],{"class":3430}," typing ",[3416,8948,6570],{"class":3634},[3416,8950,8951],{"class":3430}," Callable, TypeVar, Any\n",[3416,8953,8954],{"class":3418,"line":3475},[3416,8955,3491],{"emptyLinePlaceholder":3490},[3416,8957,8958,8961,8964],{"class":3418,"line":3487},[3416,8959,8960],{"class":3430},"T = TypeVar(",[3416,8962,8963],{"class":3500},"\"T\"",[3416,8965,3504],{"class":3430},[3416,8967,8968],{"class":3418,"line":3494},[3416,8969,3491],{"emptyLinePlaceholder":3490},[3416,8971,8972],{"class":3418,"line":3507},[3416,8973,3491],{"emptyLinePlaceholder":3490},[3416,8975,8976,8978,8981],{"class":3418,"line":3520},[3416,8977,3423],{"class":3422},[3416,8979,8980],{"class":3426}," lazy_property",[3416,8982,3431],{"class":3430},[3416,8984,8985],{"class":3418,"line":3532},[3416,8986,4046],{"class":3500},[3416,8988,8989],{"class":3418,"line":3648},[3416,8990,8991],{"class":3500},"    Non-data descriptor для ледачого обчислення атрибутів.\n",[3416,8993,8994],{"class":3418,"line":3654},[3416,8995,6891],{"class":3500},[3416,8997,8998],{"class":3418,"line":3681},[3416,8999,9000],{"class":3500},"    При першому зверненні до атрибуту викликає обгорнуту функцію,\n",[3416,9002,9003],{"class":3418,"line":3706},[3416,9004,9005],{"class":3500},"    зберігає результат у obj.__dict__ під тим самим ім'ям і повертає його.\n",[3416,9007,9008],{"class":3418,"line":3743},[3416,9009,9010],{"class":3500},"    Усі наступні звернення отримують значення напряму з __dict__,\n",[3416,9012,9013],{"class":3418,"line":3756},[3416,9014,9015],{"class":3500},"    оминаючи дескриптор повністю.\n",[3416,9017,9018],{"class":3418,"line":3782},[3416,9019,6891],{"class":3500},[3416,9021,9022],{"class":3418,"line":4157},[3416,9023,9024],{"class":3500},"    Використання:\n",[3416,9026,9027],{"class":3418,"line":4184},[3416,9028,9029],{"class":3500},"        class MyClass:\n",[3416,9031,9032],{"class":3418,"line":4190},[3416,9033,9034],{"class":3500},"            @lazy_property\n",[3416,9036,9037],{"class":3418,"line":4206},[3416,9038,9039],{"class":3500},"            def expensive_value(self) -> int:\n",[3416,9041,9042],{"class":3418,"line":4217},[3416,9043,9044],{"class":3500},"                return heavy_computation()\n",[3416,9046,9047],{"class":3418,"line":4238},[3416,9048,4046],{"class":3500},[3416,9050,9051],{"class":3418,"line":4286},[3416,9052,3491],{"emptyLinePlaceholder":3490},[3416,9054,9055,9057,9059,9061,9063,9065,9068,9071,9073],{"class":3418,"line":4294},[3416,9056,3437],{"class":3422},[3416,9058,3441],{"class":3440},[3416,9060,3444],{"class":3430},[3416,9062,3448],{"class":3447},[3416,9064,3451],{"class":3430},[3416,9066,9067],{"class":3447},"func",[3416,9069,9070],{"class":3430},": Callable) -> ",[3416,9072,3676],{"class":3422},[3416,9074,3431],{"class":3430},[3416,9076,9077,9079],{"class":3418,"line":4299},[3416,9078,3469],{"class":3422},[3416,9080,9081],{"class":3430},".func      = func\n",[3416,9083,9084,9086,9089,9091],{"class":3418,"line":4325},[3416,9085,3469],{"class":3422},[3416,9087,9088],{"class":3430},".attr_name = ",[3416,9090,3676],{"class":3422},[3416,9092,9093],{"class":3516},"           # заповниться у __set_name__\n",[3416,9095,9096,9098,9100,9103,9106,9108],{"class":3418,"line":4368},[3416,9097,3469],{"class":3422},[3416,9099,3986],{"class":3430},[3416,9101,9102],{"class":3447},"__doc__",[3416,9104,9105],{"class":3430},"   = func.",[3416,9107,9102],{"class":3447},[3416,9109,9110],{"class":3516},"   # зберігаємо документацію\n",[3416,9112,9113],{"class":3418,"line":4381},[3416,9114,3491],{"emptyLinePlaceholder":3490},[3416,9116,9117,9119,9121,9123,9125,9127,9129,9131,9133,9135,9137,9139,9141,9143,9145],{"class":3418,"line":4386},[3416,9118,3437],{"class":3422},[3416,9120,4076],{"class":3440},[3416,9122,3444],{"class":3430},[3416,9124,3448],{"class":3447},[3416,9126,3451],{"class":3430},[3416,9128,4007],{"class":3447},[3416,9130,3457],{"class":3430},[3416,9132,3726],{"class":3426},[3416,9134,3451],{"class":3430},[3416,9136,3454],{"class":3447},[3416,9138,3457],{"class":3430},[3416,9140,3460],{"class":3426},[3416,9142,3624],{"class":3430},[3416,9144,3676],{"class":3422},[3416,9146,3431],{"class":3430},[3416,9148,9149,9151],{"class":3418,"line":4391},[3416,9150,3469],{"class":3422},[3416,9152,9153],{"class":3430},".attr_name = name\n",[3416,9155,9156],{"class":3418,"line":4401},[3416,9157,3491],{"emptyLinePlaceholder":3490},[3416,9159,9160,9162,9164,9166,9168,9170,9172,9174,9176,9178,9180],{"class":3418,"line":4407},[3416,9161,3437],{"class":3422},[3416,9163,4162],{"class":3440},[3416,9165,3444],{"class":3430},[3416,9167,3448],{"class":3447},[3416,9169,3451],{"class":3430},[3416,9171,3953],{"class":3447},[3416,9173,3451],{"class":3430},[3416,9175,3960],{"class":3447},[3416,9177,4177],{"class":3430},[3416,9179,3676],{"class":3422},[3416,9181,9182],{"class":3430},") -> Any:\n",[3416,9184,9185,9187,9189,9191,9193],{"class":3418,"line":4413},[3416,9186,3684],{"class":3634},[3416,9188,4195],{"class":3430},[3416,9190,4198],{"class":3422},[3416,9192,4201],{"class":3422},[3416,9194,3431],{"class":3430},[3416,9196,9197,9199,9201],{"class":3418,"line":4419},[3416,9198,4209],{"class":3634},[3416,9200,3638],{"class":3422},[3416,9202,9203],{"class":3516},"  # доступ через клас → повертаємо сам дескриптор\n",[3416,9205,9206],{"class":3418,"line":4425},[3416,9207,3491],{"emptyLinePlaceholder":3490},[3416,9209,9210,9212,9214,9217,9219,9221],{"class":3418,"line":4430},[3416,9211,3684],{"class":3634},[3416,9213,3638],{"class":3422},[3416,9215,9216],{"class":3430},".attr_name ",[3416,9218,4198],{"class":3422},[3416,9220,4201],{"class":3422},[3416,9222,3431],{"class":3430},[3416,9224,9225,9227,9229],{"class":3418,"line":4464},[3416,9226,3709],{"class":3634},[3416,9228,3712],{"class":3426},[3416,9230,7058],{"class":3430},[3416,9232,9233],{"class":3418,"line":4475},[3416,9234,9235],{"class":3500},"                \"lazy_property не можна використовувати без __set_name__. \"\n",[3416,9237,9238],{"class":3418,"line":4485},[3416,9239,9240],{"class":3500},"                \"Переконайтесь, що він оголошений як атрибут класу.\"\n",[3416,9242,9243],{"class":3418,"line":4490},[3416,9244,7049],{"class":3430},[3416,9246,9247],{"class":3418,"line":4508},[3416,9248,3491],{"emptyLinePlaceholder":3490},[3416,9250,9251],{"class":3418,"line":4541},[3416,9252,9253],{"class":3516},"        # Обчислюємо значення і одразу записуємо у __dict__ екземпляра.\n",[3416,9255,9256],{"class":3418,"line":4546},[3416,9257,9258],{"class":3516},"        # Наступного разу Python знайде його там і не дійде до нас (non-data!).\n",[3416,9260,9261,9263,9265,9267,9270,9272,9275,9277,9280,9282,9284,9286,9288,9290,9293],{"class":3418,"line":4551},[3416,9262,4241],{"class":3440},[3416,9264,3444],{"class":3430},[3416,9266,3717],{"class":3422},[3416,9268,9269],{"class":3500},"\"  [lazy_property] Обчислюємо '",[3416,9271,4264],{"class":3422},[3416,9273,9274],{"class":3430},".attr_name",[3416,9276,3735],{"class":3422},[3416,9278,9279],{"class":3500},"' для ",[3416,9281,3723],{"class":3422},[3416,9283,3726],{"class":3426},[3416,9285,4255],{"class":3430},[3416,9287,3732],{"class":3447},[3416,9289,3735],{"class":3422},[3416,9291,9292],{"class":3500},"...\"",[3416,9294,3504],{"class":3430},[3416,9296,9297,9299,9301],{"class":3418,"line":4567},[3416,9298,4220],{"class":3430},[3416,9300,3448],{"class":3422},[3416,9302,9303],{"class":3430},".func(obj)\n",[3416,9305,9306,9308,9310,9312,9314],{"class":3418,"line":4576},[3416,9307,5435],{"class":3430},[3416,9309,4758],{"class":3447},[3416,9311,5440],{"class":3430},[3416,9313,3448],{"class":3422},[3416,9315,9316],{"class":3430},".attr_name] = value\n",[3416,9318,9319,9321],{"class":3418,"line":4608},[3416,9320,3635],{"class":3634},[3416,9322,4291],{"class":3430},[3416,9324,9325],{"class":3418,"line":4613},[3416,9326,3491],{"emptyLinePlaceholder":3490},[3416,9328,9329],{"class":3418,"line":4619},[3416,9330,9331],{"class":3516},"    # Немає __set__ → це non-data descriptor → __dict__ екземпляра має пріоритет\n",[3416,9333,9334],{"class":3418,"line":5772},[3416,9335,3491],{"emptyLinePlaceholder":3490},[3416,9337,9338],{"class":3418,"line":5781},[3416,9339,3491],{"emptyLinePlaceholder":3490},[3416,9341,9342,9344,9347],{"class":3418,"line":5806},[3416,9343,3423],{"class":3422},[3416,9345,9346],{"class":3426}," Document",[3416,9348,3431],{"class":3430},[3416,9350,9351],{"class":3418,"line":5811},[3416,9352,4046],{"class":3500},[3416,9354,9355],{"class":3418,"line":7383},[3416,9356,9357],{"class":3500},"    Документ із текстовим вмістом.\n",[3416,9359,9360],{"class":3418,"line":7412},[3416,9361,9362],{"class":3500},"    Всі похідні атрибути обчислюються ледаче — лише при першому зверненні.\n",[3416,9364,9365],{"class":3418,"line":7431},[3416,9366,4046],{"class":3500},[3416,9368,9369],{"class":3418,"line":7457},[3416,9370,3491],{"emptyLinePlaceholder":3490},[3416,9372,9373,9375,9377,9379,9381,9383,9386,9388,9390,9392,9394],{"class":3418,"line":7471},[3416,9374,3437],{"class":3422},[3416,9376,3441],{"class":3440},[3416,9378,3444],{"class":3430},[3416,9380,3448],{"class":3447},[3416,9382,3451],{"class":3430},[3416,9384,9385],{"class":3447},"text",[3416,9387,3457],{"class":3430},[3416,9389,3460],{"class":3426},[3416,9391,3624],{"class":3430},[3416,9393,3676],{"class":3422},[3416,9395,3431],{"class":3430},[3416,9397,9398,9400],{"class":3418,"line":7532},[3416,9399,3469],{"class":3422},[3416,9401,9402],{"class":3430},".text = text\n",[3416,9404,9405],{"class":3418,"line":7541},[3416,9406,3491],{"emptyLinePlaceholder":3490},[3416,9408,9409],{"class":3418,"line":7575},[3416,9410,9411],{"class":3440},"    @lazy_property\n",[3416,9413,9414,9416,9419,9421,9423,9425,9427],{"class":3418,"line":7580},[3416,9415,3437],{"class":3422},[3416,9417,9418],{"class":3440}," word_count",[3416,9420,3444],{"class":3430},[3416,9422,3448],{"class":3447},[3416,9424,3624],{"class":3430},[3416,9426,3696],{"class":3426},[3416,9428,3431],{"class":3430},[3416,9430,9431],{"class":3418,"line":7585},[3416,9432,9433],{"class":3500},"        \"\"\"Кількість слів у документі.\"\"\"\n",[3416,9435,9436,9439,9442,9445],{"class":3418,"line":7590},[3416,9437,9438],{"class":3430},"        time.sleep(",[3416,9440,9441],{"class":3483},"0.05",[3416,9443,9444],{"class":3430},")  ",[3416,9446,9447],{"class":3516},"# симуляція важкого NLP-аналізу\n",[3416,9449,9450,9452,9454,9456,9458],{"class":3418,"line":7604},[3416,9451,3635],{"class":3634},[3416,9453,7841],{"class":3440},[3416,9455,3444],{"class":3430},[3416,9457,3448],{"class":3422},[3416,9459,9460],{"class":3430},".text.split())\n",[3416,9462,9463],{"class":3418,"line":7609},[3416,9464,3491],{"emptyLinePlaceholder":3490},[3416,9466,9467],{"class":3418,"line":7615},[3416,9468,9411],{"class":3440},[3416,9470,9471,9473,9476,9478,9480,9483,9485],{"class":3418,"line":7621},[3416,9472,3437],{"class":3422},[3416,9474,9475],{"class":3440}," unique_words",[3416,9477,3444],{"class":3430},[3416,9479,3448],{"class":3447},[3416,9481,9482],{"class":3430},") -> set[",[3416,9484,3460],{"class":3426},[3416,9486,9487],{"class":3430},"]:\n",[3416,9489,9490],{"class":3418,"line":7626},[3416,9491,9492],{"class":3500},"        \"\"\"Множина унікальних слів (у нижньому регістрі).\"\"\"\n",[3416,9494,9495,9497,9499],{"class":3418,"line":7632},[3416,9496,9438],{"class":3430},[3416,9498,9441],{"class":3483},[3416,9500,3504],{"class":3430},[3416,9502,9503,9505,9508,9511,9513,9516,9519,9521,9523],{"class":3418,"line":7638},[3416,9504,3635],{"class":3634},[3416,9506,9507],{"class":3430}," {w.lower().strip(",[3416,9509,9510],{"class":3500},"\".,!?;:\"",[3416,9512,5800],{"class":3430},[3416,9514,9515],{"class":3634},"for",[3416,9517,9518],{"class":3430}," w ",[3416,9520,7039],{"class":3634},[3416,9522,3638],{"class":3422},[3416,9524,9525],{"class":3430},".text.split()}\n",[3416,9527,9528],{"class":3418,"line":7643},[3416,9529,3491],{"emptyLinePlaceholder":3490},[3416,9531,9532],{"class":3418,"line":7648},[3416,9533,9411],{"class":3440},[3416,9535,9536,9538,9541,9543,9545,9548,9550,9552,9554],{"class":3418,"line":7678},[3416,9537,3437],{"class":3422},[3416,9539,9540],{"class":3440}," char_frequency",[3416,9542,3444],{"class":3430},[3416,9544,3448],{"class":3447},[3416,9546,9547],{"class":3430},") -> dict[",[3416,9549,3460],{"class":3426},[3416,9551,3451],{"class":3430},[3416,9553,3696],{"class":3426},[3416,9555,9487],{"class":3430},[3416,9557,9558],{"class":3418,"line":7686},[3416,9559,9560],{"class":3500},"        \"\"\"Частота кожного символу (без пробілів).\"\"\"\n",[3416,9562,9563,9565,9567],{"class":3418,"line":7691},[3416,9564,9438],{"class":3430},[3416,9566,9441],{"class":3483},[3416,9568,3504],{"class":3430},[3416,9570,9571,9574,9576,9578,9580],{"class":3418,"line":7712},[3416,9572,9573],{"class":3430},"        freq: dict[",[3416,9575,3460],{"class":3426},[3416,9577,3451],{"class":3430},[3416,9579,3696],{"class":3426},[3416,9581,9582],{"class":3430},"] = {}\n",[3416,9584,9585,9588,9591,9593,9595,9598,9601,9603,9605],{"class":3418,"line":7727},[3416,9586,9587],{"class":3634},"        for",[3416,9589,9590],{"class":3430}," ch ",[3416,9592,7039],{"class":3634},[3416,9594,3638],{"class":3422},[3416,9596,9597],{"class":3430},".text.replace(",[3416,9599,9600],{"class":3500},"\" \"",[3416,9602,3451],{"class":3430},[3416,9604,8611],{"class":3500},[3416,9606,3463],{"class":3430},[3416,9608,9609,9612,9614,9617],{"class":3418,"line":7736},[3416,9610,9611],{"class":3430},"            freq[ch] = freq.get(ch, ",[3416,9613,6417],{"class":3483},[3416,9615,9616],{"class":3430},") + ",[3416,9618,9619],{"class":3483},"1\n",[3416,9621,9622,9624,9627,9629,9632,9635,9638,9640,9643,9646,9649,9652,9655,9658,9660,9662],{"class":3418,"line":7752},[3416,9623,3635],{"class":3634},[3416,9625,9626],{"class":3426}," dict",[3416,9628,3444],{"class":3430},[3416,9630,9631],{"class":3440},"sorted",[3416,9633,9634],{"class":3430},"(freq.items(), ",[3416,9636,9637],{"class":3447},"key",[3416,9639,4177],{"class":3430},[3416,9641,9642],{"class":3422},"lambda",[3416,9644,9645],{"class":3447}," x",[3416,9647,9648],{"class":3430},": x[",[3416,9650,9651],{"class":3483},"1",[3416,9653,9654],{"class":3430},"], ",[3416,9656,9657],{"class":3447},"reverse",[3416,9659,4177],{"class":3430},[3416,9661,7246],{"class":3422},[3416,9663,9664],{"class":3430},"))\n",[3416,9666,9667],{"class":3418,"line":7771},[3416,9668,3491],{"emptyLinePlaceholder":3490},[3416,9670,9671],{"class":3418,"line":7776},[3416,9672,9411],{"class":3440},[3416,9674,9675,9677,9680,9682,9684,9686,9688],{"class":3418,"line":7782},[3416,9676,3437],{"class":3422},[3416,9678,9679],{"class":3440}," reading_time_minutes",[3416,9681,3444],{"class":3430},[3416,9683,3448],{"class":3447},[3416,9685,3624],{"class":3430},[3416,9687,3627],{"class":3426},[3416,9689,3431],{"class":3430},[3416,9691,9692],{"class":3418,"line":7792},[3416,9693,9694],{"class":3500},"        \"\"\"Приблизний час читання (200 слів\u002Fхв — середня швидкість).\"\"\"\n",[3416,9696,9697,9699,9702,9704,9706,9709,9711,9713,9716],{"class":3418,"line":7801},[3416,9698,3635],{"class":3634},[3416,9700,9701],{"class":3440}," round",[3416,9703,3444],{"class":3430},[3416,9705,3448],{"class":3422},[3416,9707,9708],{"class":3430},".word_count \u002F ",[3416,9710,8270],{"class":3483},[3416,9712,3451],{"class":3430},[3416,9714,9715],{"class":3483},"2",[3416,9717,3504],{"class":3430},[3416,9719,9720],{"class":3418,"line":7817},[3416,9721,3491],{"emptyLinePlaceholder":3490},[3416,9723,9724,9726,9728,9730,9732,9734,9736],{"class":3418,"line":7822},[3416,9725,3437],{"class":3422},[3416,9727,4495],{"class":3440},[3416,9729,3444],{"class":3430},[3416,9731,3448],{"class":3447},[3416,9733,3624],{"class":3430},[3416,9735,3460],{"class":3426},[3416,9737,3431],{"class":3430},[3416,9739,9740,9742,9744,9747,9749,9751,9753,9755,9758,9760],{"class":3418,"line":7852},[3416,9741,3635],{"class":3634},[3416,9743,4513],{"class":3422},[3416,9745,9746],{"class":3500},"\"Document(",[3416,9748,3723],{"class":3422},[3416,9750,7896],{"class":3440},[3416,9752,3444],{"class":3430},[3416,9754,3448],{"class":3422},[3416,9756,9757],{"class":3430},".text)",[3416,9759,3735],{"class":3422},[3416,9761,9762],{"class":3500}," chars)\"\n",[4645,9764,9766,9775,9782,9790,9793,9800,9809,9817,9820,9827,9838,9841,9848],{"title":9765},"Демонстрація lazy_property",[4649,9767,9769,4657,9772],{"className":9768},[3418],[3416,9770,4656],{"className":9771},[4655],[3838,9773,9774],{},"python lazy_property.py",[4649,9776,9778],{"className":9777},[3418],[3416,9779,9781],{"className":9780},[4721],"# Створення документа — миттєво, нічого не обчислюється",[4649,9783,9785,9786,5221],{"className":9784},[3418],"doc = Document(",[3416,9787,9789],{"className":9788},[4683],"\"Python дескриптори — потужний механізм мови...\"",[4649,9791],{"className":9792},[3418],[4649,9794,9796],{"className":9795},[3418],[3416,9797,9799],{"className":9798},[4721],"# Перше звернення — обчислення відбувається",[4649,9801,9803,9804,9808],{"className":9802},[3418],"  [lazy_property] Обчислюємо ",[3416,9805,9807],{"className":9806},[4710],"'word_count'"," для Document...",[4649,9810,9812,9813],{"className":9811},[3418],"Кількість слів: ",[3416,9814,9816],{"className":9815},[4683],"6",[4649,9818],{"className":9819},[3418],[4649,9821,9823],{"className":9822},[3418],[3416,9824,9826],{"className":9825},[4721],"# Друге звернення — миттєво, з __dict__",[4649,9828,9830,9831,5877,9834],{"className":9829},[3418],"Знову word_count: ",[3416,9832,9816],{"className":9833},[4683],[3416,9835,9837],{"className":9836},[4721],"# __get__ не викликався!",[4649,9839],{"className":9840},[3418],[4649,9842,9844],{"className":9843},[3418],[3416,9845,9847],{"className":9846},[4721],"# Перевіряємо __dict__",[4649,9849,9851,9852],{"className":9850},[3418],"doc.__dict__: ",[3416,9853,9855],{"className":9854},[4667],"{'text': '...', 'word_count': 6}",[3407,9857,9859],{"className":3409,"code":9858,"language":3411,"meta":3412,"style":3412},"# Повна демонстрація використання\nimport time\n\ndoc = Document(\n    \"Python дескриптори — потужний механізм мови. \"\n    \"Вони лежать в основі @property, методів та ORM-полів. \"\n    \"Розуміння дескрипторів відкриває глибину Python.\"\n)\n\nprint(f\"Документ створено: {doc}\")\nprint(f\"__dict__ одразу після створення: {doc.__dict__}\\n\")\n\n# Перший доступ до word_count — обчислення\nt0 = time.perf_counter()\nwc = doc.word_count\nt1 = time.perf_counter()\nprint(f\"word_count = {wc}  (час: {(t1 - t0) * 1000:.1f}мс)\")\n\n# Другий доступ — миттєво, з __dict__\nt0 = time.perf_counter()\nwc2 = doc.word_count\nt1 = time.perf_counter()\nprint(f\"word_count = {wc2}  (час: {(t1 - t0) * 1000:.3f}мс)  ← з кешу\\n\")\n\n# unique_words — обчислюється вперше\nprint(f\"unique_words: {doc.unique_words}\\n\")\n\n# reading_time використовує word_count — той вже в __dict__, тому lazy обчислення\n# word_count не запускається заново\nprint(f\"Час читання: {doc.reading_time_minutes} хв\")\n\n# __dict__ після всіх звернень\nprint(f\"\\nОстаточний __dict__: {list(doc.__dict__.keys())}\")\n",[3398,9860,9861,9866,9872,9876,9881,9886,9891,9896,9900,9904,9926,9952,9956,9961,9966,9971,9976,10013,10017,10022,10026,10031,10035,10072,10076,10081,10105,10109,10114,10119,10142,10146,10151],{"__ignoreMap":3412},[3416,9862,9863],{"class":3418,"line":3419},[3416,9864,9865],{"class":3516},"# Повна демонстрація використання\n",[3416,9867,9868,9870],{"class":3418,"line":3434},[3416,9869,6570],{"class":3634},[3416,9871,8939],{"class":3430},[3416,9873,9874],{"class":3418,"line":3466},[3416,9875,3491],{"emptyLinePlaceholder":3490},[3416,9877,9878],{"class":3418,"line":3475},[3416,9879,9880],{"class":3430},"doc = Document(\n",[3416,9882,9883],{"class":3418,"line":3487},[3416,9884,9885],{"class":3500},"    \"Python дескриптори — потужний механізм мови. \"\n",[3416,9887,9888],{"class":3418,"line":3494},[3416,9889,9890],{"class":3500},"    \"Вони лежать в основі @property, методів та ORM-полів. \"\n",[3416,9892,9893],{"class":3418,"line":3507},[3416,9894,9895],{"class":3500},"    \"Розуміння дескрипторів відкриває глибину Python.\"\n",[3416,9897,9898],{"class":3418,"line":3520},[3416,9899,3504],{"class":3430},[3416,9901,9902],{"class":3418,"line":3532},[3416,9903,3491],{"emptyLinePlaceholder":3490},[3416,9905,9906,9908,9910,9912,9915,9917,9920,9922,9924],{"class":3418,"line":3648},[3416,9907,4570],{"class":3440},[3416,9909,3444],{"class":3430},[3416,9911,3717],{"class":3422},[3416,9913,9914],{"class":3500},"\"Документ створено: ",[3416,9916,3723],{"class":3422},[3416,9918,9919],{"class":3430},"doc",[3416,9921,3735],{"class":3422},[3416,9923,3738],{"class":3500},[3416,9925,3504],{"class":3430},[3416,9927,9928,9930,9932,9934,9937,9939,9942,9944,9946,9948,9950],{"class":3418,"line":3654},[3416,9929,4570],{"class":3440},[3416,9931,3444],{"class":3430},[3416,9933,3717],{"class":3422},[3416,9935,9936],{"class":3500},"\"__dict__ одразу після створення: ",[3416,9938,3723],{"class":3422},[3416,9940,9941],{"class":3430},"doc.",[3416,9943,4758],{"class":3447},[3416,9945,3735],{"class":3422},[3416,9947,4588],{"class":4587},[3416,9949,3738],{"class":3500},[3416,9951,3504],{"class":3430},[3416,9953,9954],{"class":3418,"line":3681},[3416,9955,3491],{"emptyLinePlaceholder":3490},[3416,9957,9958],{"class":3418,"line":3706},[3416,9959,9960],{"class":3516},"# Перший доступ до word_count — обчислення\n",[3416,9962,9963],{"class":3418,"line":3743},[3416,9964,9965],{"class":3430},"t0 = time.perf_counter()\n",[3416,9967,9968],{"class":3418,"line":3756},[3416,9969,9970],{"class":3430},"wc = doc.word_count\n",[3416,9972,9973],{"class":3418,"line":3782},[3416,9974,9975],{"class":3430},"t1 = time.perf_counter()\n",[3416,9977,9978,9980,9982,9984,9987,9989,9992,9994,9997,9999,10002,10005,10008,10011],{"class":3418,"line":4157},[3416,9979,4570],{"class":3440},[3416,9981,3444],{"class":3430},[3416,9983,3717],{"class":3422},[3416,9985,9986],{"class":3500},"\"word_count = ",[3416,9988,3723],{"class":3422},[3416,9990,9991],{"class":3430},"wc",[3416,9993,3735],{"class":3422},[3416,9995,9996],{"class":3500},"  (час: ",[3416,9998,3723],{"class":3422},[3416,10000,10001],{"class":3430},"(t1 - t0) * ",[3416,10003,10004],{"class":3483},"1000",[3416,10006,10007],{"class":3422},":.1f}",[3416,10009,10010],{"class":3500},"мс)\"",[3416,10012,3504],{"class":3430},[3416,10014,10015],{"class":3418,"line":4184},[3416,10016,3491],{"emptyLinePlaceholder":3490},[3416,10018,10019],{"class":3418,"line":4190},[3416,10020,10021],{"class":3516},"# Другий доступ — миттєво, з __dict__\n",[3416,10023,10024],{"class":3418,"line":4206},[3416,10025,9965],{"class":3430},[3416,10027,10028],{"class":3418,"line":4217},[3416,10029,10030],{"class":3430},"wc2 = doc.word_count\n",[3416,10032,10033],{"class":3418,"line":4238},[3416,10034,9975],{"class":3430},[3416,10036,10037,10039,10041,10043,10045,10047,10050,10052,10054,10056,10058,10060,10063,10066,10068,10070],{"class":3418,"line":4286},[3416,10038,4570],{"class":3440},[3416,10040,3444],{"class":3430},[3416,10042,3717],{"class":3422},[3416,10044,9986],{"class":3500},[3416,10046,3723],{"class":3422},[3416,10048,10049],{"class":3430},"wc2",[3416,10051,3735],{"class":3422},[3416,10053,9996],{"class":3500},[3416,10055,3723],{"class":3422},[3416,10057,10001],{"class":3430},[3416,10059,10004],{"class":3483},[3416,10061,10062],{"class":3422},":.3f}",[3416,10064,10065],{"class":3500},"мс)  ← з кешу",[3416,10067,4588],{"class":4587},[3416,10069,3738],{"class":3500},[3416,10071,3504],{"class":3430},[3416,10073,10074],{"class":3418,"line":4294},[3416,10075,3491],{"emptyLinePlaceholder":3490},[3416,10077,10078],{"class":3418,"line":4299},[3416,10079,10080],{"class":3516},"# unique_words — обчислюється вперше\n",[3416,10082,10083,10085,10087,10089,10092,10094,10097,10099,10101,10103],{"class":3418,"line":4325},[3416,10084,4570],{"class":3440},[3416,10086,3444],{"class":3430},[3416,10088,3717],{"class":3422},[3416,10090,10091],{"class":3500},"\"unique_words: ",[3416,10093,3723],{"class":3422},[3416,10095,10096],{"class":3430},"doc.unique_words",[3416,10098,3735],{"class":3422},[3416,10100,4588],{"class":4587},[3416,10102,3738],{"class":3500},[3416,10104,3504],{"class":3430},[3416,10106,10107],{"class":3418,"line":4368},[3416,10108,3491],{"emptyLinePlaceholder":3490},[3416,10110,10111],{"class":3418,"line":4381},[3416,10112,10113],{"class":3516},"# reading_time використовує word_count — той вже в __dict__, тому lazy обчислення\n",[3416,10115,10116],{"class":3418,"line":4386},[3416,10117,10118],{"class":3516},"# word_count не запускається заново\n",[3416,10120,10121,10123,10125,10127,10130,10132,10135,10137,10140],{"class":3418,"line":4391},[3416,10122,4570],{"class":3440},[3416,10124,3444],{"class":3430},[3416,10126,3717],{"class":3422},[3416,10128,10129],{"class":3500},"\"Час читання: ",[3416,10131,3723],{"class":3422},[3416,10133,10134],{"class":3430},"doc.reading_time_minutes",[3416,10136,3735],{"class":3422},[3416,10138,10139],{"class":3500}," хв\"",[3416,10141,3504],{"class":3430},[3416,10143,10144],{"class":3418,"line":4401},[3416,10145,3491],{"emptyLinePlaceholder":3490},[3416,10147,10148],{"class":3418,"line":4407},[3416,10149,10150],{"class":3516},"# __dict__ після всіх звернень\n",[3416,10152,10153,10155,10157,10159,10161,10163,10166,10168,10171,10174,10176,10179,10181,10183],{"class":3418,"line":4413},[3416,10154,4570],{"class":3440},[3416,10156,3444],{"class":3430},[3416,10158,3717],{"class":3422},[3416,10160,3738],{"class":3500},[3416,10162,4588],{"class":4587},[3416,10164,10165],{"class":3500},"Остаточний __dict__: ",[3416,10167,3723],{"class":3422},[3416,10169,10170],{"class":3426},"list",[3416,10172,10173],{"class":3430},"(doc.",[3416,10175,4758],{"class":3447},[3416,10177,10178],{"class":3430},".keys())",[3416,10180,3735],{"class":3422},[3416,10182,3738],{"class":3500},[3416,10184,3504],{"class":3430},[4645,10186,10188,10196,10205,10213,10216,10222,10235,10245,10248,10255,10263,10266,10273,10282,10285,10292],{"title":10187},"python lazy_full_demo.py",[4649,10189,10191,4657,10194],{"className":10190},[3418],[3416,10192,4656],{"className":10193},[4655],[3838,10195,10187],{},[4649,10197,10199,10200,10204],{"className":10198},[3418],"Документ створено: Document(",[3416,10201,10203],{"className":10202},[4667],"127"," chars)",[4649,10206,10208,10209],{"className":10207},[3418],"__dict__ одразу після створення: ",[3416,10210,10212],{"className":10211},[4721],"{'text': '...'}",[4649,10214],{"className":10215},[3418],[4649,10217,9803,10219,9808],{"className":10218},[3418],[3416,10220,9807],{"className":10221},[4710],[4649,10223,10225,10226,9996,10230,10234],{"className":10224},[3418],"word_count = ",[3416,10227,10229],{"className":10228},[4683],"21",[3416,10231,10233],{"className":10232},[4710],"51.3","мс)",[4649,10236,10225,10238,9996,10241,10065],{"className":10237},[3418],[3416,10239,10229],{"className":10240},[4683],[3416,10242,10244],{"className":10243},[4667],"0.001",[4649,10246],{"className":10247},[3418],[4649,10249,9803,10251,9808],{"className":10250},[3418],[3416,10252,10254],{"className":10253},[4710],"'unique_words'",[4649,10256,10258,10259],{"className":10257},[3418],"unique_words: ",[3416,10260,10262],{"className":10261},[4683],"{'python', 'дескриптори', 'потужний', ...}",[4649,10264],{"className":10265},[3418],[4649,10267,9803,10269,9808],{"className":10268},[3418],[3416,10270,10272],{"className":10271},[4710],"'reading_time_minutes'",[4649,10274,10276,10277,10281],{"className":10275},[3418],"Час читання: ",[3416,10278,10280],{"className":10279},[4683],"0.1"," хв",[4649,10283],{"className":10284},[3418],[4649,10286,10165,10288],{"className":10287},[3418],[3416,10289,10291],{"className":10290},[4667],"['text', 'word_count', 'unique_words', 'reading_time_minutes']",[4649,10293,10295],{"className":10294},[3418],[3416,10296,10298],{"className":10297},[4721],"# char_frequency так і не обчислився — він не знадобився",[3850,10300,10302,10303],{"id":10301},"порівняння-з-functoolscached_property","Порівняння з ",[3398,10304,10305],{},"functools.cached_property",[3394,10307,10308,10309,10311,10312,10315],{},"Python 3.8 ввів ",[3398,10310,10305],{}," — вбудовану реалізацію рівно цього патерну. Наша власна реалізація ",[3398,10313,10314],{},"lazy_property"," дидактична і показує механіку зсередини, але у production-коді слід використовувати стандартну:",[3407,10317,10319],{"className":3409,"code":10318,"language":3411,"meta":3412,"style":3412},"from functools import cached_property\n\n\nclass HeavyModel:\n    \"\"\"\n    Модель з дорогими обчисленнями — використовує вбудований cached_property.\n    \"\"\"\n\n    def __init__(self, data: list[int]) -> None:\n        self.data = data\n\n    @cached_property\n    def mean(self) -> float:\n        \"\"\"Середнє значення (обчислюється один раз).\"\"\"\n        return sum(self.data) \u002F len(self.data)\n\n    @cached_property\n    def variance(self) -> float:\n        \"\"\"Дисперсія (залежить від mean — також обчислюється один раз).\"\"\"\n        m = self.mean\n        return sum((x - m) ** 2 for x in self.data) \u002F len(self.data)\n\n    @cached_property\n    def std_deviation(self) -> float:\n        \"\"\"Стандартне відхилення.\"\"\"\n        return self.variance ** 0.5\n\n\nmodel = HeavyModel([2, 4, 4, 4, 5, 5, 7, 9])\n\nprint(f\"Середнє:             {model.mean:.2f}\")        # обчислюється\nprint(f\"Дисперсія:           {model.variance:.2f}\")    # обчислюється (mean — з кешу)\nprint(f\"Стандартне відхил.: {model.std_deviation:.2f}\") # обчислюється (variance — з кешу)\nprint(f\"Середнє знову:       {model.mean:.2f}\")        # миттєво з __dict__\n",[3398,10320,10321,10333,10337,10341,10350,10354,10359,10363,10367,10394,10401,10405,10410,10427,10432,10455,10459,10463,10480,10485,10495,10525,10529,10533,10550,10555,10567,10571,10575,10616,10620,10646,10672,10697],{"__ignoreMap":3412},[3416,10322,10323,10325,10328,10330],{"class":3418,"line":3419},[3416,10324,6564],{"class":3634},[3416,10326,10327],{"class":3430}," functools ",[3416,10329,6570],{"class":3634},[3416,10331,10332],{"class":3430}," cached_property\n",[3416,10334,10335],{"class":3418,"line":3434},[3416,10336,3491],{"emptyLinePlaceholder":3490},[3416,10338,10339],{"class":3418,"line":3466},[3416,10340,3491],{"emptyLinePlaceholder":3490},[3416,10342,10343,10345,10348],{"class":3418,"line":3475},[3416,10344,3423],{"class":3422},[3416,10346,10347],{"class":3426}," HeavyModel",[3416,10349,3431],{"class":3430},[3416,10351,10352],{"class":3418,"line":3487},[3416,10353,4046],{"class":3500},[3416,10355,10356],{"class":3418,"line":3494},[3416,10357,10358],{"class":3500},"    Модель з дорогими обчисленнями — використовує вбудований cached_property.\n",[3416,10360,10361],{"class":3418,"line":3507},[3416,10362,4046],{"class":3500},[3416,10364,10365],{"class":3418,"line":3520},[3416,10366,3491],{"emptyLinePlaceholder":3490},[3416,10368,10369,10371,10373,10375,10377,10379,10382,10385,10387,10390,10392],{"class":3418,"line":3532},[3416,10370,3437],{"class":3422},[3416,10372,3441],{"class":3440},[3416,10374,3444],{"class":3430},[3416,10376,3448],{"class":3447},[3416,10378,3451],{"class":3430},[3416,10380,10381],{"class":3447},"data",[3416,10383,10384],{"class":3430},": list[",[3416,10386,3696],{"class":3426},[3416,10388,10389],{"class":3430},"]) -> ",[3416,10391,3676],{"class":3422},[3416,10393,3431],{"class":3430},[3416,10395,10396,10398],{"class":3418,"line":3648},[3416,10397,3469],{"class":3422},[3416,10399,10400],{"class":3430},".data = data\n",[3416,10402,10403],{"class":3418,"line":3654},[3416,10404,3491],{"emptyLinePlaceholder":3490},[3416,10406,10407],{"class":3418,"line":3681},[3416,10408,10409],{"class":3440},"    @cached_property\n",[3416,10411,10412,10414,10417,10419,10421,10423,10425],{"class":3418,"line":3706},[3416,10413,3437],{"class":3422},[3416,10415,10416],{"class":3440}," mean",[3416,10418,3444],{"class":3430},[3416,10420,3448],{"class":3447},[3416,10422,3624],{"class":3430},[3416,10424,3627],{"class":3426},[3416,10426,3431],{"class":3430},[3416,10428,10429],{"class":3418,"line":3743},[3416,10430,10431],{"class":3500},"        \"\"\"Середнє значення (обчислюється один раз).\"\"\"\n",[3416,10433,10434,10436,10439,10441,10443,10446,10448,10450,10452],{"class":3418,"line":3756},[3416,10435,3635],{"class":3634},[3416,10437,10438],{"class":3440}," sum",[3416,10440,3444],{"class":3430},[3416,10442,3448],{"class":3422},[3416,10444,10445],{"class":3430},".data) \u002F ",[3416,10447,7896],{"class":3440},[3416,10449,3444],{"class":3430},[3416,10451,3448],{"class":3422},[3416,10453,10454],{"class":3430},".data)\n",[3416,10456,10457],{"class":3418,"line":3782},[3416,10458,3491],{"emptyLinePlaceholder":3490},[3416,10460,10461],{"class":3418,"line":4157},[3416,10462,10409],{"class":3440},[3416,10464,10465,10467,10470,10472,10474,10476,10478],{"class":3418,"line":4184},[3416,10466,3437],{"class":3422},[3416,10468,10469],{"class":3440}," variance",[3416,10471,3444],{"class":3430},[3416,10473,3448],{"class":3447},[3416,10475,3624],{"class":3430},[3416,10477,3627],{"class":3426},[3416,10479,3431],{"class":3430},[3416,10481,10482],{"class":3418,"line":4190},[3416,10483,10484],{"class":3500},"        \"\"\"Дисперсія (залежить від mean — також обчислюється один раз).\"\"\"\n",[3416,10486,10487,10490,10492],{"class":3418,"line":4206},[3416,10488,10489],{"class":3430},"        m = ",[3416,10491,3448],{"class":3422},[3416,10493,10494],{"class":3430},".mean\n",[3416,10496,10497,10499,10501,10504,10506,10508,10511,10513,10515,10517,10519,10521,10523],{"class":3418,"line":4217},[3416,10498,3635],{"class":3634},[3416,10500,10438],{"class":3440},[3416,10502,10503],{"class":3430},"((x - m) ** ",[3416,10505,9715],{"class":3483},[3416,10507,7033],{"class":3634},[3416,10509,10510],{"class":3430}," x ",[3416,10512,7039],{"class":3634},[3416,10514,3638],{"class":3422},[3416,10516,10445],{"class":3430},[3416,10518,7896],{"class":3440},[3416,10520,3444],{"class":3430},[3416,10522,3448],{"class":3422},[3416,10524,10454],{"class":3430},[3416,10526,10527],{"class":3418,"line":4238},[3416,10528,3491],{"emptyLinePlaceholder":3490},[3416,10530,10531],{"class":3418,"line":4286},[3416,10532,10409],{"class":3440},[3416,10534,10535,10537,10540,10542,10544,10546,10548],{"class":3418,"line":4294},[3416,10536,3437],{"class":3422},[3416,10538,10539],{"class":3440}," std_deviation",[3416,10541,3444],{"class":3430},[3416,10543,3448],{"class":3447},[3416,10545,3624],{"class":3430},[3416,10547,3627],{"class":3426},[3416,10549,3431],{"class":3430},[3416,10551,10552],{"class":3418,"line":4299},[3416,10553,10554],{"class":3500},"        \"\"\"Стандартне відхилення.\"\"\"\n",[3416,10556,10557,10559,10561,10564],{"class":3418,"line":4325},[3416,10558,3635],{"class":3634},[3416,10560,3638],{"class":3422},[3416,10562,10563],{"class":3430},".variance ** ",[3416,10565,10566],{"class":3483},"0.5\n",[3416,10568,10569],{"class":3418,"line":4368},[3416,10570,3491],{"emptyLinePlaceholder":3490},[3416,10572,10573],{"class":3418,"line":4381},[3416,10574,3491],{"emptyLinePlaceholder":3490},[3416,10576,10577,10580,10582,10584,10587,10589,10591,10593,10595,10597,10599,10601,10603,10605,10608,10610,10613],{"class":3418,"line":4386},[3416,10578,10579],{"class":3430},"model = HeavyModel([",[3416,10581,9715],{"class":3483},[3416,10583,3451],{"class":3430},[3416,10585,10586],{"class":3483},"4",[3416,10588,3451],{"class":3430},[3416,10590,10586],{"class":3483},[3416,10592,3451],{"class":3430},[3416,10594,10586],{"class":3483},[3416,10596,3451],{"class":3430},[3416,10598,4599],{"class":3483},[3416,10600,3451],{"class":3430},[3416,10602,4599],{"class":3483},[3416,10604,3451],{"class":3430},[3416,10606,10607],{"class":3483},"7",[3416,10609,3451],{"class":3430},[3416,10611,10612],{"class":3483},"9",[3416,10614,10615],{"class":3430},"])\n",[3416,10617,10618],{"class":3418,"line":4391},[3416,10619,3491],{"emptyLinePlaceholder":3490},[3416,10621,10622,10624,10626,10628,10631,10633,10636,10639,10641,10643],{"class":3418,"line":4401},[3416,10623,4570],{"class":3440},[3416,10625,3444],{"class":3430},[3416,10627,3717],{"class":3422},[3416,10629,10630],{"class":3500},"\"Середнє:             ",[3416,10632,3723],{"class":3422},[3416,10634,10635],{"class":3430},"model.mean",[3416,10637,10638],{"class":3422},":.2f}",[3416,10640,3738],{"class":3500},[3416,10642,6518],{"class":3430},[3416,10644,10645],{"class":3516},"# обчислюється\n",[3416,10647,10648,10650,10652,10654,10657,10659,10662,10664,10666,10669],{"class":3418,"line":4407},[3416,10649,4570],{"class":3440},[3416,10651,3444],{"class":3430},[3416,10653,3717],{"class":3422},[3416,10655,10656],{"class":3500},"\"Дисперсія:           ",[3416,10658,3723],{"class":3422},[3416,10660,10661],{"class":3430},"model.variance",[3416,10663,10638],{"class":3422},[3416,10665,3738],{"class":3500},[3416,10667,10668],{"class":3430},")    ",[3416,10670,10671],{"class":3516},"# обчислюється (mean — з кешу)\n",[3416,10673,10674,10676,10678,10680,10683,10685,10688,10690,10692,10694],{"class":3418,"line":4413},[3416,10675,4570],{"class":3440},[3416,10677,3444],{"class":3430},[3416,10679,3717],{"class":3422},[3416,10681,10682],{"class":3500},"\"Стандартне відхил.: ",[3416,10684,3723],{"class":3422},[3416,10686,10687],{"class":3430},"model.std_deviation",[3416,10689,10638],{"class":3422},[3416,10691,3738],{"class":3500},[3416,10693,5800],{"class":3430},[3416,10695,10696],{"class":3516},"# обчислюється (variance — з кешу)\n",[3416,10698,10699,10701,10703,10705,10708,10710,10712,10714,10716,10718],{"class":3418,"line":4419},[3416,10700,4570],{"class":3440},[3416,10702,3444],{"class":3430},[3416,10704,3717],{"class":3422},[3416,10706,10707],{"class":3500},"\"Середнє знову:       ",[3416,10709,3723],{"class":3422},[3416,10711,10635],{"class":3430},[3416,10713,10638],{"class":3422},[3416,10715,3738],{"class":3500},[3416,10717,6518],{"class":3430},[3416,10719,10720],{"class":3516},"# миттєво з __dict__\n",[10722,10723,10724,4657,10726,10729,10730,10733,10734,10737,10738,3986],"warning",{},[3398,10725,10305],{},[3838,10727,10728],{},"не є потокобезпечним"," за замовчуванням. Якщо кілька потоків одночасно звернуться до некешованого атрибуту — обчислення може відбутись кілька разів. Для потокобезпечного кешування слід використовувати блокування (",[3398,10731,10732],{},"threading.Lock",") або ",[3398,10735,10736],{},"functools.lru_cache"," на звичайному методі з ",[3398,10739,3549],{},[3850,10741,10743],{"id":10742},"скидання-кешу-коли-ледаче-значення-застаріває","Скидання кешу: коли ледаче значення застаріває",[3394,10745,10746,10747,10749,10750,10752,10753,10755],{},"Оскільки ",[3398,10748,8876],{}," (і наша ",[3398,10751,10314],{},") зберігають значення у ",[3398,10754,4894],{},", скинути кеш можна простим видаленням:",[3407,10757,10759],{"className":3409,"code":10758,"language":3411,"meta":3412,"style":3412},"doc = Document(\"Початковий текст документа\")\nprint(doc.word_count)        # обчислюється: 3\n\n# Змінюємо базові дані — кеш застарів!\ndoc.text = \"Новий набагато довший текст із великою кількістю слів у цьому документі\"\n\n# Старий кеш досі у __dict__:\nprint(doc.word_count)        # 3 — НЕПРАВИЛЬНО! читає застарілий кеш\n\n# Скидаємо кеш вручну:\ndel doc.__dict__['word_count']\n\n# Тепер обчислюється заново:\nprint(doc.word_count)        # 14 — правильно\n",[3398,10760,10761,10770,10780,10784,10789,10797,10801,10806,10815,10819,10824,10841,10845,10850],{"__ignoreMap":3412},[3416,10762,10763,10765,10768],{"class":3418,"line":3419},[3416,10764,9785],{"class":3430},[3416,10766,10767],{"class":3500},"\"Початковий текст документа\"",[3416,10769,3504],{"class":3430},[3416,10771,10772,10774,10777],{"class":3418,"line":3434},[3416,10773,4570],{"class":3440},[3416,10775,10776],{"class":3430},"(doc.word_count)        ",[3416,10778,10779],{"class":3516},"# обчислюється: 3\n",[3416,10781,10782],{"class":3418,"line":3466},[3416,10783,3491],{"emptyLinePlaceholder":3490},[3416,10785,10786],{"class":3418,"line":3475},[3416,10787,10788],{"class":3516},"# Змінюємо базові дані — кеш застарів!\n",[3416,10790,10791,10794],{"class":3418,"line":3487},[3416,10792,10793],{"class":3430},"doc.text = ",[3416,10795,10796],{"class":3500},"\"Новий набагато довший текст із великою кількістю слів у цьому документі\"\n",[3416,10798,10799],{"class":3418,"line":3494},[3416,10800,3491],{"emptyLinePlaceholder":3490},[3416,10802,10803],{"class":3418,"line":3507},[3416,10804,10805],{"class":3516},"# Старий кеш досі у __dict__:\n",[3416,10807,10808,10810,10812],{"class":3418,"line":3520},[3416,10809,4570],{"class":3440},[3416,10811,10776],{"class":3430},[3416,10813,10814],{"class":3516},"# 3 — НЕПРАВИЛЬНО! читає застарілий кеш\n",[3416,10816,10817],{"class":3418,"line":3532},[3416,10818,3491],{"emptyLinePlaceholder":3490},[3416,10820,10821],{"class":3418,"line":3648},[3416,10822,10823],{"class":3516},"# Скидаємо кеш вручну:\n",[3416,10825,10826,10829,10832,10834,10836,10838],{"class":3418,"line":3654},[3416,10827,10828],{"class":3634},"del",[3416,10830,10831],{"class":3430}," doc.",[3416,10833,4758],{"class":3447},[3416,10835,5440],{"class":3430},[3416,10837,9807],{"class":3500},[3416,10839,10840],{"class":3430},"]\n",[3416,10842,10843],{"class":3418,"line":3681},[3416,10844,3491],{"emptyLinePlaceholder":3490},[3416,10846,10847],{"class":3418,"line":3706},[3416,10848,10849],{"class":3516},"# Тепер обчислюється заново:\n",[3416,10851,10852,10854,10856],{"class":3418,"line":3743},[3416,10853,4570],{"class":3440},[3416,10855,10776],{"class":3430},[3416,10857,10858],{"class":3516},"# 14 — правильно\n",[3879,10860,10861,10862,10864,10865,10867,10868,10870,10871,10874],{},"Це відкриває важливе архітектурне питання: якщо базові дані об'єкта можуть змінюватись, ",[3398,10863,8876],{}," потребує ручного скидання кешу або замінюється на звичайний ",[3398,10866,3549],{},". ",[3398,10869,8876],{}," найбільш підходить для ",[3838,10872,10873],{},"незмінних об'єктів"," або об'єктів, де кешовані атрибути залежать лише від даних, що задаються при ініціалізації і більше не змінюються.",[3843,10876],{},[3389,10878,10880],{"id":10879},"частина-v-практичний-сценарій-3-емуляція-orm-полів","Частина V: Практичний сценарій 3 — Емуляція ORM-полів",[3850,10882,10884],{"id":10883},"декларативне-визначення-схеми-даних","Декларативне визначення схеми даних",[3394,10886,10887],{},"Одна з найпотужніших демонстрацій дескрипторів — реалізація спрощеного ORM (Object-Relational Mapper) у стилі Django. Ідея: клас Python описує таблицю бази даних декларативно, а кожне поле — це дескриптор, що несе метадані (тип колонки, обмеження, значення за замовчуванням).",[3394,10889,10890],{},"Побудуємо мінімальну, але повнофункціональну систему таких полів:",[3407,10892,10894],{"className":3409,"code":10893,"language":3411,"meta":3412,"style":3412},"# mini_orm.py\nfrom __future__ import annotations\nfrom typing import Any\n\n\n# ─── Базовий клас поля ───────────────────────────────────────────────────────\n\nclass Field:\n    \"\"\"\n    Базовий дескриптор для ORM-полів.\n    Зберігає метадані колонки: тип, обов'язковість, значення за замовчуванням.\n    При присвоєнні перевіряє тип і обов'язковість.\n    \"\"\"\n\n    # Глобальний лічильник для збереження порядку оголошення полів\n    _creation_counter: int = 0\n\n    def __init__(\n        self,\n        *,\n        null: bool = False,\n        default: Any = ...,    # Ellipsis означає «значення за замовчуванням відсутнє»\n        primary_key: bool = False,\n        db_column: str | None = None,\n    ) -> None:\n        self.null        = null\n        self.default     = default\n        self.primary_key = primary_key\n        self.db_column   = db_column    # ім'я колонки у БД (якщо відрізняється від атрибуту)\n\n        # Запам'ятовуємо порядок оголошення (як у Django)\n        Field._creation_counter += 1\n        self._order = Field._creation_counter\n\n        self.attr_name: str = \"\"        # встановлюється у __set_name__\n\n    def __set_name__(self, owner: type, name: str) -> None:\n        self.attr_name = name\n        if self.db_column is None:\n            self.db_column = name       # за замовчуванням ім'я колонки = ім'я атрибуту\n\n    def __get__(self, obj, objtype=None) -> Any:\n        if obj is None:\n            return self\n        # Повертаємо значення зі сховища екземпляра, або default\n        value = obj.__dict__.get(f\"_field_{self.attr_name}\", ...)\n        if value is ...:\n            if self.default is not ...:\n                return self.default() if callable(self.default) else self.default\n            return None\n        return value\n\n    def __set__(self, obj, value) -> None:\n        if value is None:\n            if not self.null:\n                raise ValueError(\n                    f\"Поле '{self.attr_name}' не може бути NULL \"\n                    f\"(встановіть null=True щоб дозволити)\"\n                )\n        else:\n            self.validate(value)\n        obj.__dict__[f\"_field_{self.attr_name}\"] = value\n\n    def validate(self, value: Any) -> None:\n        \"\"\"Перевірка, специфічна для типу поля. Перевизначається у підкласах.\"\"\"\n        pass\n\n    def to_sql_type(self) -> str:\n        \"\"\"SQL-тип колонки. Перевизначається у підкласах.\"\"\"\n        return \"TEXT\"\n\n    def __repr__(self) -> str:\n        return (\n            f\"{type(self).__name__}(\"\n            f\"null={self.null}, \"\n            f\"default={self.default!r}, \"\n            f\"db_column={self.db_column!r})\"\n        )\n\n\n# ─── Конкретні типи полів ─────────────────────────────────────────────────────\n\nclass IntegerField(Field):\n    \"\"\"Цілочисельна колонка. Відповідає INTEGER у SQL.\"\"\"\n\n    def __init__(self, *, min_value: int | None = None, max_value: int | None = None, **kwargs):\n        super().__init__(**kwargs)\n        self.min_value = min_value\n        self.max_value = max_value\n\n    def validate(self, value: Any) -> None:\n        if not isinstance(value, int) or isinstance(value, bool):\n            raise TypeError(\n                f\"Поле '{self.attr_name}' очікує int, отримано {type(value).__name__!r}\"\n            )\n        if self.min_value is not None and value \u003C self.min_value:\n            raise ValueError(f\"Поле '{self.attr_name}': {value} \u003C мінімум {self.min_value}\")\n        if self.max_value is not None and value > self.max_value:\n            raise ValueError(f\"Поле '{self.attr_name}': {value} > максимум {self.max_value}\")\n\n    def to_sql_type(self) -> str:\n        return \"INTEGER\"\n\n\nclass CharField(Field):\n    \"\"\"Рядкова колонка з обмеженням довжини. Відповідає VARCHAR(n) у SQL.\"\"\"\n\n    def __init__(self, max_length: int = 255, **kwargs):\n        super().__init__(**kwargs)\n        self.max_length = max_length\n\n    def validate(self, value: Any) -> None:\n        if not isinstance(value, str):\n            raise TypeError(\n                f\"Поле '{self.attr_name}' очікує str, отримано {type(value).__name__!r}\"\n            )\n        if len(value) > self.max_length:\n            raise ValueError(\n                f\"Поле '{self.attr_name}': рядок завдовжки {len(value)} \"\n                f\"перевищує max_length={self.max_length}\"\n            )\n\n    def to_sql_type(self) -> str:\n        return f\"VARCHAR({self.max_length})\"\n\n\nclass FloatField(Field):\n    \"\"\"Поле з плаваючою крапкою. Відповідає REAL \u002F DOUBLE у SQL.\"\"\"\n\n    def validate(self, value: Any) -> None:\n        if not isinstance(value, (int, float)) or isinstance(value, bool):\n            raise TypeError(\n                f\"Поле '{self.attr_name}' очікує число, отримано {type(value).__name__!r}\"\n            )\n\n    def to_sql_type(self) -> str:\n        return \"REAL\"\n\n\nclass BooleanField(Field):\n    \"\"\"Булеве поле. Відповідає BOOLEAN \u002F INTEGER(0\u002F1) у SQLite.\"\"\"\n\n    def validate(self, value: Any) -> None:\n        if not isinstance(value, bool):\n            raise TypeError(\n                f\"Поле '{self.attr_name}' очікує bool, отримано {type(value).__name__!r}\"\n            )\n\n    def to_sql_type(self) -> str:\n        return \"BOOLEAN\"\n",[3398,10895,10896,10901,10914,10925,10929,10933,10938,10942,10951,10955,10960,10965,10970,10974,10978,10983,10995,10999,11007,11013,11017,11033,11044,11059,11078,11086,11093,11100,11107,11117,11121,11126,11133,11140,11144,11160,11164,11196,11202,11217,11228,11232,11256,11268,11274,11279,11304,11316,11332,11361,11368,11374,11378,11402,11414,11425,11434,11451,11458,11463,11470,11476,11498,11502,11523,11528,11533,11537,11554,11559,11566,11570,11586,11592,11616,11632,11648,11664,11668,11672,11676,11681,11685,11699,11704,11708,11761,11774,11781,11788,11792,11812,11837,11846,11874,11879,11904,11946,11972,12013,12018,12035,12043,12048,12053,12067,12073,12078,12108,12119,12126,12131,12152,12167,12176,12204,12209,12223,12232,12260,12276,12281,12286,12303,12321,12326,12331,12345,12351,12356,12377,12407,12416,12444,12449,12454,12471,12479,12484,12489,12503,12509,12514,12535,12550,12559,12587,12592,12597,12614],{"__ignoreMap":3412},[3416,10897,10898],{"class":3418,"line":3419},[3416,10899,10900],{"class":3516},"# mini_orm.py\n",[3416,10902,10903,10905,10908,10911],{"class":3418,"line":3434},[3416,10904,6564],{"class":3634},[3416,10906,10907],{"class":3447}," __future__",[3416,10909,10910],{"class":3634}," import",[3416,10912,10913],{"class":3430}," annotations\n",[3416,10915,10916,10918,10920,10922],{"class":3418,"line":3466},[3416,10917,6564],{"class":3634},[3416,10919,8946],{"class":3430},[3416,10921,6570],{"class":3634},[3416,10923,10924],{"class":3430}," Any\n",[3416,10926,10927],{"class":3418,"line":3475},[3416,10928,3491],{"emptyLinePlaceholder":3490},[3416,10930,10931],{"class":3418,"line":3487},[3416,10932,3491],{"emptyLinePlaceholder":3490},[3416,10934,10935],{"class":3418,"line":3494},[3416,10936,10937],{"class":3516},"# ─── Базовий клас поля ───────────────────────────────────────────────────────\n",[3416,10939,10940],{"class":3418,"line":3507},[3416,10941,3491],{"emptyLinePlaceholder":3490},[3416,10943,10944,10946,10949],{"class":3418,"line":3520},[3416,10945,3423],{"class":3422},[3416,10947,10948],{"class":3426}," Field",[3416,10950,3431],{"class":3430},[3416,10952,10953],{"class":3418,"line":3532},[3416,10954,4046],{"class":3500},[3416,10956,10957],{"class":3418,"line":3648},[3416,10958,10959],{"class":3500},"    Базовий дескриптор для ORM-полів.\n",[3416,10961,10962],{"class":3418,"line":3654},[3416,10963,10964],{"class":3500},"    Зберігає метадані колонки: тип, обов'язковість, значення за замовчуванням.\n",[3416,10966,10967],{"class":3418,"line":3681},[3416,10968,10969],{"class":3500},"    При присвоєнні перевіряє тип і обов'язковість.\n",[3416,10971,10972],{"class":3418,"line":3706},[3416,10973,4046],{"class":3500},[3416,10975,10976],{"class":3418,"line":3743},[3416,10977,3491],{"emptyLinePlaceholder":3490},[3416,10979,10980],{"class":3418,"line":3756},[3416,10981,10982],{"class":3516},"    # Глобальний лічильник для збереження порядку оголошення полів\n",[3416,10984,10985,10988,10990,10992],{"class":3418,"line":3782},[3416,10986,10987],{"class":3430},"    _creation_counter: ",[3416,10989,3696],{"class":3426},[3416,10991,4355],{"class":3430},[3416,10993,10994],{"class":3483},"0\n",[3416,10996,10997],{"class":3418,"line":4157},[3416,10998,3491],{"emptyLinePlaceholder":3490},[3416,11000,11001,11003,11005],{"class":3418,"line":4184},[3416,11002,3437],{"class":3422},[3416,11004,3441],{"class":3440},[3416,11006,7058],{"class":3430},[3416,11008,11009,11011],{"class":3418,"line":4190},[3416,11010,3469],{"class":3447},[3416,11012,7188],{"class":3430},[3416,11014,11015],{"class":3418,"line":4206},[3416,11016,7231],{"class":3430},[3416,11018,11019,11022,11024,11026,11028,11031],{"class":3418,"line":4217},[3416,11020,11021],{"class":3447},"        null",[3416,11023,3457],{"class":3430},[3416,11025,7241],{"class":3426},[3416,11027,4355],{"class":3430},[3416,11029,11030],{"class":3422},"False",[3416,11032,7188],{"class":3430},[3416,11034,11035,11038,11041],{"class":3418,"line":4238},[3416,11036,11037],{"class":3447},"        default",[3416,11039,11040],{"class":3430},": Any = ...,    ",[3416,11042,11043],{"class":3516},"# Ellipsis означає «значення за замовчуванням відсутнє»\n",[3416,11045,11046,11049,11051,11053,11055,11057],{"class":3418,"line":4286},[3416,11047,11048],{"class":3447},"        primary_key",[3416,11050,3457],{"class":3430},[3416,11052,7241],{"class":3426},[3416,11054,4355],{"class":3430},[3416,11056,11030],{"class":3422},[3416,11058,7188],{"class":3430},[3416,11060,11061,11064,11066,11068,11070,11072,11074,11076],{"class":3418,"line":4294},[3416,11062,11063],{"class":3447},"        db_column",[3416,11065,3457],{"class":3430},[3416,11067,3460],{"class":3426},[3416,11069,6931],{"class":3430},[3416,11071,3676],{"class":3422},[3416,11073,4355],{"class":3430},[3416,11075,3676],{"class":3422},[3416,11077,7188],{"class":3430},[3416,11079,11080,11082,11084],{"class":3418,"line":4299},[3416,11081,8105],{"class":3430},[3416,11083,3676],{"class":3422},[3416,11085,3431],{"class":3430},[3416,11087,11088,11090],{"class":3418,"line":4325},[3416,11089,3469],{"class":3422},[3416,11091,11092],{"class":3430},".null        = null\n",[3416,11094,11095,11097],{"class":3418,"line":4368},[3416,11096,3469],{"class":3422},[3416,11098,11099],{"class":3430},".default     = default\n",[3416,11101,11102,11104],{"class":3418,"line":4381},[3416,11103,3469],{"class":3422},[3416,11105,11106],{"class":3430},".primary_key = primary_key\n",[3416,11108,11109,11111,11114],{"class":3418,"line":4386},[3416,11110,3469],{"class":3422},[3416,11112,11113],{"class":3430},".db_column   = db_column    ",[3416,11115,11116],{"class":3516},"# ім'я колонки у БД (якщо відрізняється від атрибуту)\n",[3416,11118,11119],{"class":3418,"line":4391},[3416,11120,3491],{"emptyLinePlaceholder":3490},[3416,11122,11123],{"class":3418,"line":4401},[3416,11124,11125],{"class":3516},"        # Запам'ятовуємо порядок оголошення (як у Django)\n",[3416,11127,11128,11131],{"class":3418,"line":4407},[3416,11129,11130],{"class":3430},"        Field._creation_counter += ",[3416,11132,9619],{"class":3483},[3416,11134,11135,11137],{"class":3418,"line":4413},[3416,11136,3469],{"class":3422},[3416,11138,11139],{"class":3430},"._order = Field._creation_counter\n",[3416,11141,11142],{"class":3418,"line":4419},[3416,11143,3491],{"emptyLinePlaceholder":3490},[3416,11145,11146,11148,11151,11153,11155,11157],{"class":3418,"line":4425},[3416,11147,3469],{"class":3422},[3416,11149,11150],{"class":3430},".attr_name: ",[3416,11152,3460],{"class":3426},[3416,11154,4355],{"class":3430},[3416,11156,8611],{"class":3500},[3416,11158,11159],{"class":3516},"        # встановлюється у __set_name__\n",[3416,11161,11162],{"class":3418,"line":4430},[3416,11163,3491],{"emptyLinePlaceholder":3490},[3416,11165,11166,11168,11170,11172,11174,11176,11178,11180,11182,11184,11186,11188,11190,11192,11194],{"class":3418,"line":4464},[3416,11167,3437],{"class":3422},[3416,11169,4076],{"class":3440},[3416,11171,3444],{"class":3430},[3416,11173,3448],{"class":3447},[3416,11175,3451],{"class":3430},[3416,11177,4007],{"class":3447},[3416,11179,3457],{"class":3430},[3416,11181,3726],{"class":3426},[3416,11183,3451],{"class":3430},[3416,11185,3454],{"class":3447},[3416,11187,3457],{"class":3430},[3416,11189,3460],{"class":3426},[3416,11191,3624],{"class":3430},[3416,11193,3676],{"class":3422},[3416,11195,3431],{"class":3430},[3416,11197,11198,11200],{"class":3418,"line":4475},[3416,11199,3469],{"class":3422},[3416,11201,9153],{"class":3430},[3416,11203,11204,11206,11208,11211,11213,11215],{"class":3418,"line":4485},[3416,11205,3684],{"class":3634},[3416,11207,3638],{"class":3422},[3416,11209,11210],{"class":3430},".db_column ",[3416,11212,4198],{"class":3422},[3416,11214,4201],{"class":3422},[3416,11216,3431],{"class":3430},[3416,11218,11219,11222,11225],{"class":3418,"line":4490},[3416,11220,11221],{"class":3422},"            self",[3416,11223,11224],{"class":3430},".db_column = name       ",[3416,11226,11227],{"class":3516},"# за замовчуванням ім'я колонки = ім'я атрибуту\n",[3416,11229,11230],{"class":3418,"line":4508},[3416,11231,3491],{"emptyLinePlaceholder":3490},[3416,11233,11234,11236,11238,11240,11242,11244,11246,11248,11250,11252,11254],{"class":3418,"line":4541},[3416,11235,3437],{"class":3422},[3416,11237,4162],{"class":3440},[3416,11239,3444],{"class":3430},[3416,11241,3448],{"class":3447},[3416,11243,3451],{"class":3430},[3416,11245,3953],{"class":3447},[3416,11247,3451],{"class":3430},[3416,11249,3960],{"class":3447},[3416,11251,4177],{"class":3430},[3416,11253,3676],{"class":3422},[3416,11255,9182],{"class":3430},[3416,11257,11258,11260,11262,11264,11266],{"class":3418,"line":4546},[3416,11259,3684],{"class":3634},[3416,11261,4195],{"class":3430},[3416,11263,4198],{"class":3422},[3416,11265,4201],{"class":3422},[3416,11267,3431],{"class":3430},[3416,11269,11270,11272],{"class":3418,"line":4551},[3416,11271,4209],{"class":3634},[3416,11273,5361],{"class":3422},[3416,11275,11276],{"class":3418,"line":4567},[3416,11277,11278],{"class":3516},"        # Повертаємо значення зі сховища екземпляра, або default\n",[3416,11280,11281,11284,11286,11288,11290,11293,11295,11297,11299,11301],{"class":3418,"line":4576},[3416,11282,11283],{"class":3430},"        value = obj.",[3416,11285,4758],{"class":3447},[3416,11287,5373],{"class":3430},[3416,11289,3717],{"class":3422},[3416,11291,11292],{"class":3500},"\"_field_",[3416,11294,4264],{"class":3422},[3416,11296,9274],{"class":3430},[3416,11298,3735],{"class":3422},[3416,11300,3738],{"class":3500},[3416,11302,11303],{"class":3430},", ...)\n",[3416,11305,11306,11308,11311,11313],{"class":3418,"line":4608},[3416,11307,3684],{"class":3634},[3416,11309,11310],{"class":3430}," value ",[3416,11312,4198],{"class":3422},[3416,11314,11315],{"class":3430}," ...:\n",[3416,11317,11318,11321,11323,11326,11328,11330],{"class":3418,"line":4613},[3416,11319,11320],{"class":3634},"            if",[3416,11322,3638],{"class":3422},[3416,11324,11325],{"class":3430},".default ",[3416,11327,4198],{"class":3422},[3416,11329,3687],{"class":3422},[3416,11331,11315],{"class":3430},[3416,11333,11334,11337,11339,11342,11344,11347,11349,11351,11354,11356,11358],{"class":3418,"line":4619},[3416,11335,11336],{"class":3634},"                return",[3416,11338,3638],{"class":3422},[3416,11340,11341],{"class":3430},".default() ",[3416,11343,7393],{"class":3634},[3416,11345,11346],{"class":3440}," callable",[3416,11348,3444],{"class":3430},[3416,11350,3448],{"class":3422},[3416,11352,11353],{"class":3430},".default) ",[3416,11355,7401],{"class":3634},[3416,11357,3638],{"class":3422},[3416,11359,11360],{"class":3430},".default\n",[3416,11362,11363,11365],{"class":3418,"line":5772},[3416,11364,4209],{"class":3634},[3416,11366,11367],{"class":3422}," None\n",[3416,11369,11370,11372],{"class":3418,"line":5781},[3416,11371,3635],{"class":3634},[3416,11373,4291],{"class":3430},[3416,11375,11376],{"class":3418,"line":5806},[3416,11377,3491],{"emptyLinePlaceholder":3490},[3416,11379,11380,11382,11384,11386,11388,11390,11392,11394,11396,11398,11400],{"class":3418,"line":5811},[3416,11381,3437],{"class":3422},[3416,11383,4304],{"class":3440},[3416,11385,3444],{"class":3430},[3416,11387,3448],{"class":3447},[3416,11389,3451],{"class":3430},[3416,11391,3953],{"class":3447},[3416,11393,3451],{"class":3430},[3416,11395,3667],{"class":3447},[3416,11397,3624],{"class":3430},[3416,11399,3676],{"class":3422},[3416,11401,3431],{"class":3430},[3416,11403,11404,11406,11408,11410,11412],{"class":3418,"line":7383},[3416,11405,3684],{"class":3634},[3416,11407,11310],{"class":3430},[3416,11409,4198],{"class":3422},[3416,11411,4201],{"class":3422},[3416,11413,3431],{"class":3430},[3416,11415,11416,11418,11420,11422],{"class":3418,"line":7412},[3416,11417,11320],{"class":3634},[3416,11419,3687],{"class":3422},[3416,11421,3638],{"class":3422},[3416,11423,11424],{"class":3430},".null:\n",[3416,11426,11427,11430,11432],{"class":3418,"line":7431},[3416,11428,11429],{"class":3634},"                raise",[3416,11431,3761],{"class":3426},[3416,11433,7058],{"class":3430},[3416,11435,11436,11439,11442,11444,11446,11448],{"class":3418,"line":7457},[3416,11437,11438],{"class":3422},"                    f",[3416,11440,11441],{"class":3500},"\"Поле '",[3416,11443,4264],{"class":3422},[3416,11445,9274],{"class":3430},[3416,11447,3735],{"class":3422},[3416,11449,11450],{"class":3500},"' не може бути NULL \"\n",[3416,11452,11453,11455],{"class":3418,"line":7471},[3416,11454,11438],{"class":3422},[3416,11456,11457],{"class":3500},"\"(встановіть null=True щоб дозволити)\"\n",[3416,11459,11460],{"class":3418,"line":7532},[3416,11461,11462],{"class":3430},"                )\n",[3416,11464,11465,11468],{"class":3418,"line":7541},[3416,11466,11467],{"class":3634},"        else",[3416,11469,3431],{"class":3430},[3416,11471,11472,11474],{"class":3418,"line":7575},[3416,11473,11221],{"class":3422},[3416,11475,6779],{"class":3430},[3416,11477,11478,11480,11482,11484,11486,11488,11490,11492,11494,11496],{"class":3418,"line":7580},[3416,11479,5435],{"class":3430},[3416,11481,4758],{"class":3447},[3416,11483,5440],{"class":3430},[3416,11485,3717],{"class":3422},[3416,11487,11292],{"class":3500},[3416,11489,4264],{"class":3422},[3416,11491,9274],{"class":3430},[3416,11493,3735],{"class":3422},[3416,11495,3738],{"class":3500},[3416,11497,5445],{"class":3430},[3416,11499,11500],{"class":3418,"line":7585},[3416,11501,3491],{"emptyLinePlaceholder":3490},[3416,11503,11504,11506,11508,11510,11512,11514,11516,11519,11521],{"class":3418,"line":7590},[3416,11505,3437],{"class":3422},[3416,11507,6805],{"class":3440},[3416,11509,3444],{"class":3430},[3416,11511,3448],{"class":3447},[3416,11513,3451],{"class":3430},[3416,11515,3667],{"class":3447},[3416,11517,11518],{"class":3430},": Any) -> ",[3416,11520,3676],{"class":3422},[3416,11522,3431],{"class":3430},[3416,11524,11525],{"class":3418,"line":7604},[3416,11526,11527],{"class":3500},"        \"\"\"Перевірка, специфічна для типу поля. Перевизначається у підкласах.\"\"\"\n",[3416,11529,11530],{"class":3418,"line":7609},[3416,11531,11532],{"class":3634},"        pass\n",[3416,11534,11535],{"class":3418,"line":7615},[3416,11536,3491],{"emptyLinePlaceholder":3490},[3416,11538,11539,11541,11544,11546,11548,11550,11552],{"class":3418,"line":7621},[3416,11540,3437],{"class":3422},[3416,11542,11543],{"class":3440}," to_sql_type",[3416,11545,3444],{"class":3430},[3416,11547,3448],{"class":3447},[3416,11549,3624],{"class":3430},[3416,11551,3460],{"class":3426},[3416,11553,3431],{"class":3430},[3416,11555,11556],{"class":3418,"line":7626},[3416,11557,11558],{"class":3500},"        \"\"\"SQL-тип колонки. Перевизначається у підкласах.\"\"\"\n",[3416,11560,11561,11563],{"class":3418,"line":7632},[3416,11562,3635],{"class":3634},[3416,11564,11565],{"class":3500}," \"TEXT\"\n",[3416,11567,11568],{"class":3418,"line":7638},[3416,11569,3491],{"emptyLinePlaceholder":3490},[3416,11571,11572,11574,11576,11578,11580,11582,11584],{"class":3418,"line":7643},[3416,11573,3437],{"class":3422},[3416,11575,4495],{"class":3440},[3416,11577,3444],{"class":3430},[3416,11579,3448],{"class":3447},[3416,11581,3624],{"class":3430},[3416,11583,3460],{"class":3426},[3416,11585,3431],{"class":3430},[3416,11587,11588,11590],{"class":3418,"line":7648},[3416,11589,3635],{"class":3634},[3416,11591,8164],{"class":3430},[3416,11593,11594,11596,11598,11600,11602,11604,11606,11609,11611,11613],{"class":3418,"line":7678},[3416,11595,8169],{"class":3422},[3416,11597,3738],{"class":3500},[3416,11599,3723],{"class":3422},[3416,11601,3726],{"class":3426},[3416,11603,3444],{"class":3430},[3416,11605,3448],{"class":3422},[3416,11607,11608],{"class":3430},").",[3416,11610,3732],{"class":3447},[3416,11612,3735],{"class":3422},[3416,11614,11615],{"class":3500},"(\"\n",[3416,11617,11618,11620,11623,11625,11628,11630],{"class":3418,"line":7686},[3416,11619,8169],{"class":3422},[3416,11621,11622],{"class":3500},"\"null=",[3416,11624,4264],{"class":3422},[3416,11626,11627],{"class":3430},".null",[3416,11629,3735],{"class":3422},[3416,11631,7085],{"class":3500},[3416,11633,11634,11636,11639,11641,11644,11646],{"class":3418,"line":7691},[3416,11635,8169],{"class":3422},[3416,11637,11638],{"class":3500},"\"default=",[3416,11640,4264],{"class":3422},[3416,11642,11643],{"class":3430},".default",[3416,11645,4279],{"class":3422},[3416,11647,7085],{"class":3500},[3416,11649,11650,11652,11655,11657,11660,11662],{"class":3418,"line":7712},[3416,11651,8169],{"class":3422},[3416,11653,11654],{"class":3500},"\"db_column=",[3416,11656,4264],{"class":3422},[3416,11658,11659],{"class":3430},".db_column",[3416,11661,4279],{"class":3422},[3416,11663,7529],{"class":3500},[3416,11665,11666],{"class":3418,"line":7727},[3416,11667,8236],{"class":3430},[3416,11669,11670],{"class":3418,"line":7736},[3416,11671,3491],{"emptyLinePlaceholder":3490},[3416,11673,11674],{"class":3418,"line":7752},[3416,11675,3491],{"emptyLinePlaceholder":3490},[3416,11677,11678],{"class":3418,"line":7771},[3416,11679,11680],{"class":3516},"# ─── Конкретні типи полів ─────────────────────────────────────────────────────\n",[3416,11682,11683],{"class":3418,"line":7776},[3416,11684,3491],{"emptyLinePlaceholder":3490},[3416,11686,11687,11689,11692,11694,11697],{"class":3418,"line":7782},[3416,11688,3423],{"class":3422},[3416,11690,11691],{"class":3426}," IntegerField",[3416,11693,3444],{"class":3430},[3416,11695,11696],{"class":3426},"Field",[3416,11698,3463],{"class":3430},[3416,11700,11701],{"class":3418,"line":7792},[3416,11702,11703],{"class":3500},"    \"\"\"Цілочисельна колонка. Відповідає INTEGER у SQL.\"\"\"\n",[3416,11705,11706],{"class":3418,"line":7801},[3416,11707,3491],{"emptyLinePlaceholder":3490},[3416,11709,11710,11712,11714,11716,11718,11721,11724,11726,11728,11730,11732,11734,11736,11738,11741,11743,11745,11747,11749,11751,11753,11756,11759],{"class":3418,"line":7817},[3416,11711,3437],{"class":3422},[3416,11713,3441],{"class":3440},[3416,11715,3444],{"class":3430},[3416,11717,3448],{"class":3447},[3416,11719,11720],{"class":3430},", *, ",[3416,11722,11723],{"class":3447},"min_value",[3416,11725,3457],{"class":3430},[3416,11727,3696],{"class":3426},[3416,11729,6931],{"class":3430},[3416,11731,3676],{"class":3422},[3416,11733,4355],{"class":3430},[3416,11735,3676],{"class":3422},[3416,11737,3451],{"class":3430},[3416,11739,11740],{"class":3447},"max_value",[3416,11742,3457],{"class":3430},[3416,11744,3696],{"class":3426},[3416,11746,6931],{"class":3430},[3416,11748,3676],{"class":3422},[3416,11750,4355],{"class":3430},[3416,11752,3676],{"class":3422},[3416,11754,11755],{"class":3430},", **",[3416,11757,11758],{"class":3447},"kwargs",[3416,11760,3463],{"class":3430},[3416,11762,11763,11766,11769,11771],{"class":3418,"line":7822},[3416,11764,11765],{"class":3426},"        super",[3416,11767,11768],{"class":3430},"().",[3416,11770,4905],{"class":3440},[3416,11772,11773],{"class":3430},"(**kwargs)\n",[3416,11775,11776,11778],{"class":3418,"line":7852},[3416,11777,3469],{"class":3422},[3416,11779,11780],{"class":3430},".min_value = min_value\n",[3416,11782,11783,11785],{"class":3418,"line":7861},[3416,11784,3469],{"class":3422},[3416,11786,11787],{"class":3430},".max_value = max_value\n",[3416,11789,11790],{"class":3418,"line":7877},[3416,11791,3491],{"emptyLinePlaceholder":3490},[3416,11793,11794,11796,11798,11800,11802,11804,11806,11808,11810],{"class":3418,"line":7906},[3416,11795,3437],{"class":3422},[3416,11797,6805],{"class":3440},[3416,11799,3444],{"class":3430},[3416,11801,3448],{"class":3447},[3416,11803,3451],{"class":3430},[3416,11805,3667],{"class":3447},[3416,11807,11518],{"class":3430},[3416,11809,3676],{"class":3422},[3416,11811,3431],{"class":3430},[3416,11813,11815,11817,11819,11821,11823,11825,11827,11829,11831,11833,11835],{"class":3418,"line":11814},92,[3416,11816,3684],{"class":3634},[3416,11818,3687],{"class":3422},[3416,11820,3690],{"class":3440},[3416,11822,6978],{"class":3430},[3416,11824,3696],{"class":3426},[3416,11826,5800],{"class":3430},[3416,11828,7465],{"class":3422},[3416,11830,3690],{"class":3440},[3416,11832,6978],{"class":3430},[3416,11834,7241],{"class":3426},[3416,11836,3463],{"class":3430},[3416,11838,11840,11842,11844],{"class":3418,"line":11839},93,[3416,11841,3709],{"class":3634},[3416,11843,3712],{"class":3426},[3416,11845,7058],{"class":3430},[3416,11847,11849,11851,11853,11855,11857,11859,11862,11864,11866,11868,11870,11872],{"class":3418,"line":11848},94,[3416,11850,7063],{"class":3422},[3416,11852,11441],{"class":3500},[3416,11854,4264],{"class":3422},[3416,11856,9274],{"class":3430},[3416,11858,3735],{"class":3422},[3416,11860,11861],{"class":3500},"' очікує int, отримано ",[3416,11863,3723],{"class":3422},[3416,11865,3726],{"class":3426},[3416,11867,3729],{"class":3430},[3416,11869,3732],{"class":3447},[3416,11871,4279],{"class":3422},[3416,11873,6676],{"class":3500},[3416,11875,11877],{"class":3418,"line":11876},95,[3416,11878,7049],{"class":3430},[3416,11880,11882,11884,11886,11889,11891,11893,11895,11897,11899,11901],{"class":3418,"line":11881},96,[3416,11883,3684],{"class":3634},[3416,11885,3638],{"class":3422},[3416,11887,11888],{"class":3430},".min_value ",[3416,11890,4198],{"class":3422},[3416,11892,3687],{"class":3422},[3416,11894,4201],{"class":3422},[3416,11896,7838],{"class":3422},[3416,11898,6414],{"class":3430},[3416,11900,3448],{"class":3422},[3416,11902,11903],{"class":3430},".min_value:\n",[3416,11905,11907,11909,11911,11913,11915,11917,11919,11921,11923,11926,11928,11930,11932,11935,11937,11940,11942,11944],{"class":3418,"line":11906},97,[3416,11908,3709],{"class":3634},[3416,11910,3761],{"class":3426},[3416,11912,3444],{"class":3430},[3416,11914,3717],{"class":3422},[3416,11916,11441],{"class":3500},[3416,11918,4264],{"class":3422},[3416,11920,9274],{"class":3430},[3416,11922,3735],{"class":3422},[3416,11924,11925],{"class":3500},"': ",[3416,11927,3723],{"class":3422},[3416,11929,3667],{"class":3430},[3416,11931,3735],{"class":3422},[3416,11933,11934],{"class":3500}," \u003C мінімум ",[3416,11936,4264],{"class":3422},[3416,11938,11939],{"class":3430},".min_value",[3416,11941,3735],{"class":3422},[3416,11943,3738],{"class":3500},[3416,11945,3504],{"class":3430},[3416,11947,11949,11951,11953,11956,11958,11960,11962,11964,11967,11969],{"class":3418,"line":11948},98,[3416,11950,3684],{"class":3634},[3416,11952,3638],{"class":3422},[3416,11954,11955],{"class":3430},".max_value ",[3416,11957,4198],{"class":3422},[3416,11959,3687],{"class":3422},[3416,11961,4201],{"class":3422},[3416,11963,7838],{"class":3422},[3416,11965,11966],{"class":3430}," value > ",[3416,11968,3448],{"class":3422},[3416,11970,11971],{"class":3430},".max_value:\n",[3416,11973,11975,11977,11979,11981,11983,11985,11987,11989,11991,11993,11995,11997,11999,12002,12004,12007,12009,12011],{"class":3418,"line":11974},99,[3416,11976,3709],{"class":3634},[3416,11978,3761],{"class":3426},[3416,11980,3444],{"class":3430},[3416,11982,3717],{"class":3422},[3416,11984,11441],{"class":3500},[3416,11986,4264],{"class":3422},[3416,11988,9274],{"class":3430},[3416,11990,3735],{"class":3422},[3416,11992,11925],{"class":3500},[3416,11994,3723],{"class":3422},[3416,11996,3667],{"class":3430},[3416,11998,3735],{"class":3422},[3416,12000,12001],{"class":3500}," > максимум ",[3416,12003,4264],{"class":3422},[3416,12005,12006],{"class":3430},".max_value",[3416,12008,3735],{"class":3422},[3416,12010,3738],{"class":3500},[3416,12012,3504],{"class":3430},[3416,12014,12016],{"class":3418,"line":12015},100,[3416,12017,3491],{"emptyLinePlaceholder":3490},[3416,12019,12021,12023,12025,12027,12029,12031,12033],{"class":3418,"line":12020},101,[3416,12022,3437],{"class":3422},[3416,12024,11543],{"class":3440},[3416,12026,3444],{"class":3430},[3416,12028,3448],{"class":3447},[3416,12030,3624],{"class":3430},[3416,12032,3460],{"class":3426},[3416,12034,3431],{"class":3430},[3416,12036,12038,12040],{"class":3418,"line":12037},102,[3416,12039,3635],{"class":3634},[3416,12041,12042],{"class":3500}," \"INTEGER\"\n",[3416,12044,12046],{"class":3418,"line":12045},103,[3416,12047,3491],{"emptyLinePlaceholder":3490},[3416,12049,12051],{"class":3418,"line":12050},104,[3416,12052,3491],{"emptyLinePlaceholder":3490},[3416,12054,12056,12058,12061,12063,12065],{"class":3418,"line":12055},105,[3416,12057,3423],{"class":3422},[3416,12059,12060],{"class":3426}," CharField",[3416,12062,3444],{"class":3430},[3416,12064,11696],{"class":3426},[3416,12066,3463],{"class":3430},[3416,12068,12070],{"class":3418,"line":12069},106,[3416,12071,12072],{"class":3500},"    \"\"\"Рядкова колонка з обмеженням довжини. Відповідає VARCHAR(n) у SQL.\"\"\"\n",[3416,12074,12076],{"class":3418,"line":12075},107,[3416,12077,3491],{"emptyLinePlaceholder":3490},[3416,12079,12081,12083,12085,12087,12089,12091,12093,12095,12097,12099,12102,12104,12106],{"class":3418,"line":12080},108,[3416,12082,3437],{"class":3422},[3416,12084,3441],{"class":3440},[3416,12086,3444],{"class":3430},[3416,12088,3448],{"class":3447},[3416,12090,3451],{"class":3430},[3416,12092,7661],{"class":3447},[3416,12094,3457],{"class":3430},[3416,12096,3696],{"class":3426},[3416,12098,4355],{"class":3430},[3416,12100,12101],{"class":3483},"255",[3416,12103,11755],{"class":3430},[3416,12105,11758],{"class":3447},[3416,12107,3463],{"class":3430},[3416,12109,12111,12113,12115,12117],{"class":3418,"line":12110},109,[3416,12112,11765],{"class":3426},[3416,12114,11768],{"class":3430},[3416,12116,4905],{"class":3440},[3416,12118,11773],{"class":3430},[3416,12120,12122,12124],{"class":3418,"line":12121},110,[3416,12123,3469],{"class":3422},[3416,12125,7683],{"class":3430},[3416,12127,12129],{"class":3418,"line":12128},111,[3416,12130,3491],{"emptyLinePlaceholder":3490},[3416,12132,12134,12136,12138,12140,12142,12144,12146,12148,12150],{"class":3418,"line":12133},112,[3416,12135,3437],{"class":3422},[3416,12137,6805],{"class":3440},[3416,12139,3444],{"class":3430},[3416,12141,3448],{"class":3447},[3416,12143,3451],{"class":3430},[3416,12145,3667],{"class":3447},[3416,12147,11518],{"class":3430},[3416,12149,3676],{"class":3422},[3416,12151,3431],{"class":3430},[3416,12153,12155,12157,12159,12161,12163,12165],{"class":3418,"line":12154},113,[3416,12156,3684],{"class":3634},[3416,12158,3687],{"class":3422},[3416,12160,3690],{"class":3440},[3416,12162,6978],{"class":3430},[3416,12164,3460],{"class":3426},[3416,12166,3463],{"class":3430},[3416,12168,12170,12172,12174],{"class":3418,"line":12169},114,[3416,12171,3709],{"class":3634},[3416,12173,3712],{"class":3426},[3416,12175,7058],{"class":3430},[3416,12177,12179,12181,12183,12185,12187,12189,12192,12194,12196,12198,12200,12202],{"class":3418,"line":12178},115,[3416,12180,7063],{"class":3422},[3416,12182,11441],{"class":3500},[3416,12184,4264],{"class":3422},[3416,12186,9274],{"class":3430},[3416,12188,3735],{"class":3422},[3416,12190,12191],{"class":3500},"' очікує str, отримано ",[3416,12193,3723],{"class":3422},[3416,12195,3726],{"class":3426},[3416,12197,3729],{"class":3430},[3416,12199,3732],{"class":3447},[3416,12201,4279],{"class":3422},[3416,12203,6676],{"class":3500},[3416,12205,12207],{"class":3418,"line":12206},116,[3416,12208,7049],{"class":3430},[3416,12210,12212,12214,12216,12219,12221],{"class":3418,"line":12211},117,[3416,12213,3684],{"class":3634},[3416,12215,7841],{"class":3440},[3416,12217,12218],{"class":3430},"(value) > ",[3416,12220,3448],{"class":3422},[3416,12222,7849],{"class":3430},[3416,12224,12226,12228,12230],{"class":3418,"line":12225},118,[3416,12227,3709],{"class":3634},[3416,12229,3761],{"class":3426},[3416,12231,7058],{"class":3430},[3416,12233,12235,12237,12239,12241,12243,12245,12248,12250,12252,12255,12257],{"class":3418,"line":12234},119,[3416,12236,7063],{"class":3422},[3416,12238,11441],{"class":3500},[3416,12240,4264],{"class":3422},[3416,12242,9274],{"class":3430},[3416,12244,3735],{"class":3422},[3416,12246,12247],{"class":3500},"': рядок завдовжки ",[3416,12249,3723],{"class":3422},[3416,12251,7896],{"class":3440},[3416,12253,12254],{"class":3430},"(value)",[3416,12256,3735],{"class":3422},[3416,12258,12259],{"class":3500}," \"\n",[3416,12261,12263,12265,12268,12270,12272,12274],{"class":3418,"line":12262},120,[3416,12264,7063],{"class":3422},[3416,12266,12267],{"class":3500},"\"перевищує max_length=",[3416,12269,4264],{"class":3422},[3416,12271,7886],{"class":3430},[3416,12273,3735],{"class":3422},[3416,12275,6676],{"class":3500},[3416,12277,12279],{"class":3418,"line":12278},121,[3416,12280,7049],{"class":3430},[3416,12282,12284],{"class":3418,"line":12283},122,[3416,12285,3491],{"emptyLinePlaceholder":3490},[3416,12287,12289,12291,12293,12295,12297,12299,12301],{"class":3418,"line":12288},123,[3416,12290,3437],{"class":3422},[3416,12292,11543],{"class":3440},[3416,12294,3444],{"class":3430},[3416,12296,3448],{"class":3447},[3416,12298,3624],{"class":3430},[3416,12300,3460],{"class":3426},[3416,12302,3431],{"class":3430},[3416,12304,12306,12308,12310,12313,12315,12317,12319],{"class":3418,"line":12305},124,[3416,12307,3635],{"class":3634},[3416,12309,4513],{"class":3422},[3416,12311,12312],{"class":3500},"\"VARCHAR(",[3416,12314,4264],{"class":3422},[3416,12316,7886],{"class":3430},[3416,12318,3735],{"class":3422},[3416,12320,7529],{"class":3500},[3416,12322,12324],{"class":3418,"line":12323},125,[3416,12325,3491],{"emptyLinePlaceholder":3490},[3416,12327,12329],{"class":3418,"line":12328},126,[3416,12330,3491],{"emptyLinePlaceholder":3490},[3416,12332,12334,12336,12339,12341,12343],{"class":3418,"line":12333},127,[3416,12335,3423],{"class":3422},[3416,12337,12338],{"class":3426}," FloatField",[3416,12340,3444],{"class":3430},[3416,12342,11696],{"class":3426},[3416,12344,3463],{"class":3430},[3416,12346,12348],{"class":3418,"line":12347},128,[3416,12349,12350],{"class":3500},"    \"\"\"Поле з плаваючою крапкою. Відповідає REAL \u002F DOUBLE у SQL.\"\"\"\n",[3416,12352,12354],{"class":3418,"line":12353},129,[3416,12355,3491],{"emptyLinePlaceholder":3490},[3416,12357,12359,12361,12363,12365,12367,12369,12371,12373,12375],{"class":3418,"line":12358},130,[3416,12360,3437],{"class":3422},[3416,12362,6805],{"class":3440},[3416,12364,3444],{"class":3430},[3416,12366,3448],{"class":3447},[3416,12368,3451],{"class":3430},[3416,12370,3667],{"class":3447},[3416,12372,11518],{"class":3430},[3416,12374,3676],{"class":3422},[3416,12376,3431],{"class":3430},[3416,12378,12380,12382,12384,12386,12388,12390,12392,12394,12397,12399,12401,12403,12405],{"class":3418,"line":12379},131,[3416,12381,3684],{"class":3634},[3416,12383,3687],{"class":3422},[3416,12385,3690],{"class":3440},[3416,12387,3693],{"class":3430},[3416,12389,3696],{"class":3426},[3416,12391,3451],{"class":3430},[3416,12393,3627],{"class":3426},[3416,12395,12396],{"class":3430},")) ",[3416,12398,7465],{"class":3422},[3416,12400,3690],{"class":3440},[3416,12402,6978],{"class":3430},[3416,12404,7241],{"class":3426},[3416,12406,3463],{"class":3430},[3416,12408,12410,12412,12414],{"class":3418,"line":12409},132,[3416,12411,3709],{"class":3634},[3416,12413,3712],{"class":3426},[3416,12415,7058],{"class":3430},[3416,12417,12419,12421,12423,12425,12427,12429,12432,12434,12436,12438,12440,12442],{"class":3418,"line":12418},133,[3416,12420,7063],{"class":3422},[3416,12422,11441],{"class":3500},[3416,12424,4264],{"class":3422},[3416,12426,9274],{"class":3430},[3416,12428,3735],{"class":3422},[3416,12430,12431],{"class":3500},"' очікує число, отримано ",[3416,12433,3723],{"class":3422},[3416,12435,3726],{"class":3426},[3416,12437,3729],{"class":3430},[3416,12439,3732],{"class":3447},[3416,12441,4279],{"class":3422},[3416,12443,6676],{"class":3500},[3416,12445,12447],{"class":3418,"line":12446},134,[3416,12448,7049],{"class":3430},[3416,12450,12452],{"class":3418,"line":12451},135,[3416,12453,3491],{"emptyLinePlaceholder":3490},[3416,12455,12457,12459,12461,12463,12465,12467,12469],{"class":3418,"line":12456},136,[3416,12458,3437],{"class":3422},[3416,12460,11543],{"class":3440},[3416,12462,3444],{"class":3430},[3416,12464,3448],{"class":3447},[3416,12466,3624],{"class":3430},[3416,12468,3460],{"class":3426},[3416,12470,3431],{"class":3430},[3416,12472,12474,12476],{"class":3418,"line":12473},137,[3416,12475,3635],{"class":3634},[3416,12477,12478],{"class":3500}," \"REAL\"\n",[3416,12480,12482],{"class":3418,"line":12481},138,[3416,12483,3491],{"emptyLinePlaceholder":3490},[3416,12485,12487],{"class":3418,"line":12486},139,[3416,12488,3491],{"emptyLinePlaceholder":3490},[3416,12490,12492,12494,12497,12499,12501],{"class":3418,"line":12491},140,[3416,12493,3423],{"class":3422},[3416,12495,12496],{"class":3426}," BooleanField",[3416,12498,3444],{"class":3430},[3416,12500,11696],{"class":3426},[3416,12502,3463],{"class":3430},[3416,12504,12506],{"class":3418,"line":12505},141,[3416,12507,12508],{"class":3500},"    \"\"\"Булеве поле. Відповідає BOOLEAN \u002F INTEGER(0\u002F1) у SQLite.\"\"\"\n",[3416,12510,12512],{"class":3418,"line":12511},142,[3416,12513,3491],{"emptyLinePlaceholder":3490},[3416,12515,12517,12519,12521,12523,12525,12527,12529,12531,12533],{"class":3418,"line":12516},143,[3416,12518,3437],{"class":3422},[3416,12520,6805],{"class":3440},[3416,12522,3444],{"class":3430},[3416,12524,3448],{"class":3447},[3416,12526,3451],{"class":3430},[3416,12528,3667],{"class":3447},[3416,12530,11518],{"class":3430},[3416,12532,3676],{"class":3422},[3416,12534,3431],{"class":3430},[3416,12536,12538,12540,12542,12544,12546,12548],{"class":3418,"line":12537},144,[3416,12539,3684],{"class":3634},[3416,12541,3687],{"class":3422},[3416,12543,3690],{"class":3440},[3416,12545,6978],{"class":3430},[3416,12547,7241],{"class":3426},[3416,12549,3463],{"class":3430},[3416,12551,12553,12555,12557],{"class":3418,"line":12552},145,[3416,12554,3709],{"class":3634},[3416,12556,3712],{"class":3426},[3416,12558,7058],{"class":3430},[3416,12560,12562,12564,12566,12568,12570,12572,12575,12577,12579,12581,12583,12585],{"class":3418,"line":12561},146,[3416,12563,7063],{"class":3422},[3416,12565,11441],{"class":3500},[3416,12567,4264],{"class":3422},[3416,12569,9274],{"class":3430},[3416,12571,3735],{"class":3422},[3416,12573,12574],{"class":3500},"' очікує bool, отримано ",[3416,12576,3723],{"class":3422},[3416,12578,3726],{"class":3426},[3416,12580,3729],{"class":3430},[3416,12582,3732],{"class":3447},[3416,12584,4279],{"class":3422},[3416,12586,6676],{"class":3500},[3416,12588,12590],{"class":3418,"line":12589},147,[3416,12591,7049],{"class":3430},[3416,12593,12595],{"class":3418,"line":12594},148,[3416,12596,3491],{"emptyLinePlaceholder":3490},[3416,12598,12600,12602,12604,12606,12608,12610,12612],{"class":3418,"line":12599},149,[3416,12601,3437],{"class":3422},[3416,12603,11543],{"class":3440},[3416,12605,3444],{"class":3430},[3416,12607,3448],{"class":3447},[3416,12609,3624],{"class":3430},[3416,12611,3460],{"class":3426},[3416,12613,3431],{"class":3430},[3416,12615,12617,12619],{"class":3418,"line":12616},150,[3416,12618,3635],{"class":3634},[3416,12620,12621],{"class":3500}," \"BOOLEAN\"\n",[3850,12623,12625],{"id":12624},"метаклас-для-збору-метаданих-схеми","Метаклас для збору метаданих схеми",[3394,12627,12628,12629,3550],{},"Щоб клас-модель «знав» про всі свої поля (для генерації SQL, серіалізації тощо), потрібно зібрати їх при оголошенні класу. Це природна задача для ",[3838,12630,12631],{},"метакласу",[3407,12633,12635],{"className":3409,"code":12634,"language":3411,"meta":3412,"style":3412},"# mini_orm.py (продовження)\n\nclass ModelMeta(type):\n    \"\"\"\n    Метаклас для ORM-моделей.\n    При створенні класу збирає всі Field-атрибути у впорядкований словник _fields.\n    \"\"\"\n\n    def __new__(mcs, name: str, bases: tuple, namespace: dict) -> type:\n        fields: dict[str, Field] = {}\n\n        # Збираємо поля з батьківських класів (успадкування моделей)\n        for base in bases:\n            if hasattr(base, \"_fields\"):\n                fields.update(base._fields)\n\n        # Збираємо поля поточного класу (у порядку оголошення)\n        for attr_name, value in namespace.items():\n            if isinstance(value, Field):\n                fields[attr_name] = value\n\n        # Сортуємо за порядком оголошення (через _creation_counter)\n        namespace[\"_fields\"] = dict(\n            sorted(fields.items(), key=lambda item: item[1]._order)\n        )\n\n        return super().__new__(mcs, name, bases, namespace)\n\n\nclass Model(metaclass=ModelMeta):\n    \"\"\"\n    Базовий клас для всіх ORM-моделей.\n    Надає загальний __init__, __repr__, to_dict() та generate_sql().\n    \"\"\"\n\n    def __init__(self, **kwargs: Any) -> None:\n        # Встановлюємо значення полів через дескриптори (з валідацією)\n        for field_name, field in self._fields.items():\n            if field_name in kwargs:\n                setattr(self, field_name, kwargs[field_name])\n            elif field.default is not ...:\n                pass    # __get__ поверне default автоматично\n            elif not field.null:\n                raise ValueError(\n                    f\"Обов'язкове поле '{field_name}' не передано у конструктор\"\n                )\n\n    def __repr__(self) -> str:\n        parts = []\n        for name in self._fields:\n            val = getattr(self, name)\n            parts.append(f\"{name}={val!r}\")\n        return f\"{type(self).__name__}({', '.join(parts)})\"\n\n    def to_dict(self) -> dict[str, Any]:\n        \"\"\"Серіалізація у словник (для JSON, API-відповідей тощо).\"\"\"\n        return {name: getattr(self, name) for name in self._fields}\n\n    @classmethod\n    def generate_create_table_sql(cls) -> str:\n        \"\"\"Генерує SQL-запит CREATE TABLE на основі оголошених полів.\"\"\"\n        table_name = cls.__name__.lower() + \"s\"  # проста конвенція: User → users\n        columns: list[str] = []\n        for field_name, field in cls._fields.items():\n            col  = f\"    {field.db_column} {field.to_sql_type()}\"\n            if field.primary_key:\n                col += \" PRIMARY KEY AUTOINCREMENT\"\n            if not field.null and not field.primary_key:\n                col += \" NOT NULL\"\n            if field.default is not ... and not callable(field.default):\n                default_val = (\n                    f\"'{field.default}'\" if isinstance(field.default, str)\n                    else str(field.default).upper() if isinstance(field.default, bool)\n                    else str(field.default)\n                )\n                col += f\" DEFAULT {default_val}\"\n            columns.append(col)\n        cols_sql = \",\\n\".join(columns)\n        return f\"CREATE TABLE IF NOT EXISTS {table_name} (\\n{cols_sql}\\n);\"\n",[3398,12636,12637,12642,12646,12659,12663,12668,12673,12677,12681,12726,12736,12740,12745,12757,12772,12777,12781,12786,12798,12807,12812,12816,12821,12834,12859,12863,12867,12882,12886,12890,12909,12913,12918,12923,12927,12931,12951,12956,12970,12982,12994,13008,13016,13025,13033,13050,13054,13058,13074,13079,13093,13107,13135,13171,13175,13193,13198,13225,13229,13236,13254,13259,13279,13289,13302,13329,13336,13344,13360,13367,13389,13394,13422,13443,13452,13456,13474,13479,13494],{"__ignoreMap":3412},[3416,12638,12639],{"class":3418,"line":3419},[3416,12640,12641],{"class":3516},"# mini_orm.py (продовження)\n",[3416,12643,12644],{"class":3418,"line":3434},[3416,12645,3491],{"emptyLinePlaceholder":3490},[3416,12647,12648,12650,12653,12655,12657],{"class":3418,"line":3466},[3416,12649,3423],{"class":3422},[3416,12651,12652],{"class":3426}," ModelMeta",[3416,12654,3444],{"class":3430},[3416,12656,3726],{"class":3426},[3416,12658,3463],{"class":3430},[3416,12660,12661],{"class":3418,"line":3475},[3416,12662,4046],{"class":3500},[3416,12664,12665],{"class":3418,"line":3487},[3416,12666,12667],{"class":3500},"    Метаклас для ORM-моделей.\n",[3416,12669,12670],{"class":3418,"line":3494},[3416,12671,12672],{"class":3500},"    При створенні класу збирає всі Field-атрибути у впорядкований словник _fields.\n",[3416,12674,12675],{"class":3418,"line":3507},[3416,12676,4046],{"class":3500},[3416,12678,12679],{"class":3418,"line":3520},[3416,12680,3491],{"emptyLinePlaceholder":3490},[3416,12682,12683,12685,12688,12690,12693,12695,12697,12699,12701,12703,12706,12708,12710,12712,12715,12717,12720,12722,12724],{"class":3418,"line":3532},[3416,12684,3437],{"class":3422},[3416,12686,12687],{"class":3440}," __new__",[3416,12689,3444],{"class":3430},[3416,12691,12692],{"class":3447},"mcs",[3416,12694,3451],{"class":3430},[3416,12696,3454],{"class":3447},[3416,12698,3457],{"class":3430},[3416,12700,3460],{"class":3426},[3416,12702,3451],{"class":3430},[3416,12704,12705],{"class":3447},"bases",[3416,12707,3457],{"class":3430},[3416,12709,6934],{"class":3426},[3416,12711,3451],{"class":3430},[3416,12713,12714],{"class":3447},"namespace",[3416,12716,3457],{"class":3430},[3416,12718,12719],{"class":3426},"dict",[3416,12721,3624],{"class":3430},[3416,12723,3726],{"class":3426},[3416,12725,3431],{"class":3430},[3416,12727,12728,12731,12733],{"class":3418,"line":3648},[3416,12729,12730],{"class":3430},"        fields: dict[",[3416,12732,3460],{"class":3426},[3416,12734,12735],{"class":3430},", Field] = {}\n",[3416,12737,12738],{"class":3418,"line":3654},[3416,12739,3491],{"emptyLinePlaceholder":3490},[3416,12741,12742],{"class":3418,"line":3681},[3416,12743,12744],{"class":3516},"        # Збираємо поля з батьківських класів (успадкування моделей)\n",[3416,12746,12747,12749,12752,12754],{"class":3418,"line":3706},[3416,12748,9587],{"class":3634},[3416,12750,12751],{"class":3430}," base ",[3416,12753,7039],{"class":3634},[3416,12755,12756],{"class":3430}," bases:\n",[3416,12758,12759,12761,12764,12767,12770],{"class":3418,"line":3743},[3416,12760,11320],{"class":3634},[3416,12762,12763],{"class":3440}," hasattr",[3416,12765,12766],{"class":3430},"(base, ",[3416,12768,12769],{"class":3500},"\"_fields\"",[3416,12771,3463],{"class":3430},[3416,12773,12774],{"class":3418,"line":3756},[3416,12775,12776],{"class":3430},"                fields.update(base._fields)\n",[3416,12778,12779],{"class":3418,"line":3782},[3416,12780,3491],{"emptyLinePlaceholder":3490},[3416,12782,12783],{"class":3418,"line":4157},[3416,12784,12785],{"class":3516},"        # Збираємо поля поточного класу (у порядку оголошення)\n",[3416,12787,12788,12790,12793,12795],{"class":3418,"line":4184},[3416,12789,9587],{"class":3634},[3416,12791,12792],{"class":3430}," attr_name, value ",[3416,12794,7039],{"class":3634},[3416,12796,12797],{"class":3430}," namespace.items():\n",[3416,12799,12800,12802,12804],{"class":3418,"line":4190},[3416,12801,11320],{"class":3634},[3416,12803,3690],{"class":3440},[3416,12805,12806],{"class":3430},"(value, Field):\n",[3416,12808,12809],{"class":3418,"line":4206},[3416,12810,12811],{"class":3430},"                fields[attr_name] = value\n",[3416,12813,12814],{"class":3418,"line":4217},[3416,12815,3491],{"emptyLinePlaceholder":3490},[3416,12817,12818],{"class":3418,"line":4238},[3416,12819,12820],{"class":3516},"        # Сортуємо за порядком оголошення (через _creation_counter)\n",[3416,12822,12823,12826,12828,12830,12832],{"class":3418,"line":4286},[3416,12824,12825],{"class":3430},"        namespace[",[3416,12827,12769],{"class":3500},[3416,12829,5683],{"class":3430},[3416,12831,12719],{"class":3426},[3416,12833,7058],{"class":3430},[3416,12835,12836,12839,12842,12844,12846,12848,12851,12854,12856],{"class":3418,"line":4294},[3416,12837,12838],{"class":3440},"            sorted",[3416,12840,12841],{"class":3430},"(fields.items(), ",[3416,12843,9637],{"class":3447},[3416,12845,4177],{"class":3430},[3416,12847,9642],{"class":3422},[3416,12849,12850],{"class":3447}," item",[3416,12852,12853],{"class":3430},": item[",[3416,12855,9651],{"class":3483},[3416,12857,12858],{"class":3430},"]._order)\n",[3416,12860,12861],{"class":3418,"line":4299},[3416,12862,8236],{"class":3430},[3416,12864,12865],{"class":3418,"line":4325},[3416,12866,3491],{"emptyLinePlaceholder":3490},[3416,12868,12869,12871,12874,12876,12879],{"class":3418,"line":4368},[3416,12870,3635],{"class":3634},[3416,12872,12873],{"class":3426}," super",[3416,12875,11768],{"class":3430},[3416,12877,12878],{"class":3440},"__new__",[3416,12880,12881],{"class":3430},"(mcs, name, bases, namespace)\n",[3416,12883,12884],{"class":3418,"line":4381},[3416,12885,3491],{"emptyLinePlaceholder":3490},[3416,12887,12888],{"class":3418,"line":4386},[3416,12889,3491],{"emptyLinePlaceholder":3490},[3416,12891,12892,12894,12897,12899,12902,12904,12907],{"class":3418,"line":4391},[3416,12893,3423],{"class":3422},[3416,12895,12896],{"class":3426}," Model",[3416,12898,3444],{"class":3430},[3416,12900,12901],{"class":3426},"metaclass",[3416,12903,4177],{"class":3430},[3416,12905,12906],{"class":3426},"ModelMeta",[3416,12908,3463],{"class":3430},[3416,12910,12911],{"class":3418,"line":4401},[3416,12912,4046],{"class":3500},[3416,12914,12915],{"class":3418,"line":4407},[3416,12916,12917],{"class":3500},"    Базовий клас для всіх ORM-моделей.\n",[3416,12919,12920],{"class":3418,"line":4413},[3416,12921,12922],{"class":3500},"    Надає загальний __init__, __repr__, to_dict() та generate_sql().\n",[3416,12924,12925],{"class":3418,"line":4419},[3416,12926,4046],{"class":3500},[3416,12928,12929],{"class":3418,"line":4425},[3416,12930,3491],{"emptyLinePlaceholder":3490},[3416,12932,12933,12935,12937,12939,12941,12943,12945,12947,12949],{"class":3418,"line":4430},[3416,12934,3437],{"class":3422},[3416,12936,3441],{"class":3440},[3416,12938,3444],{"class":3430},[3416,12940,3448],{"class":3447},[3416,12942,11755],{"class":3430},[3416,12944,11758],{"class":3447},[3416,12946,11518],{"class":3430},[3416,12948,3676],{"class":3422},[3416,12950,3431],{"class":3430},[3416,12952,12953],{"class":3418,"line":4464},[3416,12954,12955],{"class":3516},"        # Встановлюємо значення полів через дескриптори (з валідацією)\n",[3416,12957,12958,12960,12963,12965,12967],{"class":3418,"line":4475},[3416,12959,9587],{"class":3634},[3416,12961,12962],{"class":3430}," field_name, field ",[3416,12964,7039],{"class":3634},[3416,12966,3638],{"class":3422},[3416,12968,12969],{"class":3430},"._fields.items():\n",[3416,12971,12972,12974,12977,12979],{"class":3418,"line":4485},[3416,12973,11320],{"class":3634},[3416,12975,12976],{"class":3430}," field_name ",[3416,12978,7039],{"class":3422},[3416,12980,12981],{"class":3430}," kwargs:\n",[3416,12983,12984,12987,12989,12991],{"class":3418,"line":4490},[3416,12985,12986],{"class":3440},"                setattr",[3416,12988,3444],{"class":3430},[3416,12990,3448],{"class":3422},[3416,12992,12993],{"class":3430},", field_name, kwargs[field_name])\n",[3416,12995,12996,12999,13002,13004,13006],{"class":3418,"line":4508},[3416,12997,12998],{"class":3634},"            elif",[3416,13000,13001],{"class":3430}," field.default ",[3416,13003,4198],{"class":3422},[3416,13005,3687],{"class":3422},[3416,13007,11315],{"class":3430},[3416,13009,13010,13013],{"class":3418,"line":4541},[3416,13011,13012],{"class":3634},"                pass",[3416,13014,13015],{"class":3516},"    # __get__ поверне default автоматично\n",[3416,13017,13018,13020,13022],{"class":3418,"line":4546},[3416,13019,12998],{"class":3634},[3416,13021,3687],{"class":3422},[3416,13023,13024],{"class":3430}," field.null:\n",[3416,13026,13027,13029,13031],{"class":3418,"line":4551},[3416,13028,11429],{"class":3634},[3416,13030,3761],{"class":3426},[3416,13032,7058],{"class":3430},[3416,13034,13035,13037,13040,13042,13045,13047],{"class":3418,"line":4567},[3416,13036,11438],{"class":3422},[3416,13038,13039],{"class":3500},"\"Обов'язкове поле '",[3416,13041,3723],{"class":3422},[3416,13043,13044],{"class":3430},"field_name",[3416,13046,3735],{"class":3422},[3416,13048,13049],{"class":3500},"' не передано у конструктор\"\n",[3416,13051,13052],{"class":3418,"line":4576},[3416,13053,11462],{"class":3430},[3416,13055,13056],{"class":3418,"line":4608},[3416,13057,3491],{"emptyLinePlaceholder":3490},[3416,13059,13060,13062,13064,13066,13068,13070,13072],{"class":3418,"line":4613},[3416,13061,3437],{"class":3422},[3416,13063,4495],{"class":3440},[3416,13065,3444],{"class":3430},[3416,13067,3448],{"class":3447},[3416,13069,3624],{"class":3430},[3416,13071,3460],{"class":3426},[3416,13073,3431],{"class":3430},[3416,13075,13076],{"class":3418,"line":4619},[3416,13077,13078],{"class":3430},"        parts = []\n",[3416,13080,13081,13083,13086,13088,13090],{"class":3418,"line":5772},[3416,13082,9587],{"class":3634},[3416,13084,13085],{"class":3430}," name ",[3416,13087,7039],{"class":3634},[3416,13089,3638],{"class":3422},[3416,13091,13092],{"class":3430},"._fields:\n",[3416,13094,13095,13098,13100,13102,13104],{"class":3418,"line":5781},[3416,13096,13097],{"class":3430},"            val = ",[3416,13099,4223],{"class":3440},[3416,13101,3444],{"class":3430},[3416,13103,3448],{"class":3422},[3416,13105,13106],{"class":3430},", name)\n",[3416,13108,13109,13112,13114,13116,13118,13120,13122,13124,13126,13129,13131,13133],{"class":3418,"line":5806},[3416,13110,13111],{"class":3430},"            parts.append(",[3416,13113,3717],{"class":3422},[3416,13115,3738],{"class":3500},[3416,13117,3723],{"class":3422},[3416,13119,3454],{"class":3430},[3416,13121,3735],{"class":3422},[3416,13123,4177],{"class":3500},[3416,13125,3723],{"class":3422},[3416,13127,13128],{"class":3430},"val",[3416,13130,4279],{"class":3422},[3416,13132,3738],{"class":3500},[3416,13134,3504],{"class":3430},[3416,13136,13137,13139,13141,13143,13145,13147,13149,13151,13153,13155,13157,13159,13161,13164,13167,13169],{"class":3418,"line":5811},[3416,13138,3635],{"class":3634},[3416,13140,4513],{"class":3422},[3416,13142,3738],{"class":3500},[3416,13144,3723],{"class":3422},[3416,13146,3726],{"class":3426},[3416,13148,3444],{"class":3430},[3416,13150,3448],{"class":3422},[3416,13152,11608],{"class":3430},[3416,13154,3732],{"class":3447},[3416,13156,3735],{"class":3422},[3416,13158,3444],{"class":3500},[3416,13160,3723],{"class":3422},[3416,13162,13163],{"class":3500},"', '",[3416,13165,13166],{"class":3430},".join(parts)",[3416,13168,3735],{"class":3422},[3416,13170,7529],{"class":3500},[3416,13172,13173],{"class":3418,"line":7383},[3416,13174,3491],{"emptyLinePlaceholder":3490},[3416,13176,13177,13179,13182,13184,13186,13188,13190],{"class":3418,"line":7412},[3416,13178,3437],{"class":3422},[3416,13180,13181],{"class":3440}," to_dict",[3416,13183,3444],{"class":3430},[3416,13185,3448],{"class":3447},[3416,13187,9547],{"class":3430},[3416,13189,3460],{"class":3426},[3416,13191,13192],{"class":3430},", Any]:\n",[3416,13194,13195],{"class":3418,"line":7431},[3416,13196,13197],{"class":3500},"        \"\"\"Серіалізація у словник (для JSON, API-відповідей тощо).\"\"\"\n",[3416,13199,13200,13202,13205,13207,13209,13211,13214,13216,13218,13220,13222],{"class":3418,"line":7457},[3416,13201,3635],{"class":3634},[3416,13203,13204],{"class":3430}," {name: ",[3416,13206,4223],{"class":3440},[3416,13208,3444],{"class":3430},[3416,13210,3448],{"class":3422},[3416,13212,13213],{"class":3430},", name) ",[3416,13215,9515],{"class":3634},[3416,13217,13085],{"class":3430},[3416,13219,7039],{"class":3634},[3416,13221,3638],{"class":3422},[3416,13223,13224],{"class":3430},"._fields}\n",[3416,13226,13227],{"class":3418,"line":7471},[3416,13228,3491],{"emptyLinePlaceholder":3490},[3416,13230,13231,13233],{"class":3418,"line":7532},[3416,13232,3607],{"class":3440},[3416,13234,13235],{"class":3426},"classmethod\n",[3416,13237,13238,13240,13243,13245,13248,13250,13252],{"class":3418,"line":7541},[3416,13239,3437],{"class":3422},[3416,13241,13242],{"class":3440}," generate_create_table_sql",[3416,13244,3444],{"class":3430},[3416,13246,13247],{"class":3447},"cls",[3416,13249,3624],{"class":3430},[3416,13251,3460],{"class":3426},[3416,13253,3431],{"class":3430},[3416,13255,13256],{"class":3418,"line":7575},[3416,13257,13258],{"class":3500},"        \"\"\"Генерує SQL-запит CREATE TABLE на основі оголошених полів.\"\"\"\n",[3416,13260,13261,13264,13266,13268,13270,13273,13276],{"class":3418,"line":7580},[3416,13262,13263],{"class":3430},"        table_name = ",[3416,13265,13247],{"class":3422},[3416,13267,3986],{"class":3430},[3416,13269,3732],{"class":3447},[3416,13271,13272],{"class":3430},".lower() + ",[3416,13274,13275],{"class":3500},"\"s\"",[3416,13277,13278],{"class":3516},"  # проста конвенція: User → users\n",[3416,13280,13281,13284,13286],{"class":3418,"line":7585},[3416,13282,13283],{"class":3430},"        columns: list[",[3416,13285,3460],{"class":3426},[3416,13287,13288],{"class":3430},"] = []\n",[3416,13290,13291,13293,13295,13297,13300],{"class":3418,"line":7590},[3416,13292,9587],{"class":3634},[3416,13294,12962],{"class":3430},[3416,13296,7039],{"class":3634},[3416,13298,13299],{"class":3422}," cls",[3416,13301,12969],{"class":3430},[3416,13303,13304,13307,13309,13312,13314,13317,13319,13322,13325,13327],{"class":3418,"line":7604},[3416,13305,13306],{"class":3430},"            col  = ",[3416,13308,3717],{"class":3422},[3416,13310,13311],{"class":3500},"\"    ",[3416,13313,3723],{"class":3422},[3416,13315,13316],{"class":3430},"field.db_column",[3416,13318,3735],{"class":3422},[3416,13320,13321],{"class":3422}," {",[3416,13323,13324],{"class":3430},"field.to_sql_type()",[3416,13326,3735],{"class":3422},[3416,13328,6676],{"class":3500},[3416,13330,13331,13333],{"class":3418,"line":7609},[3416,13332,11320],{"class":3634},[3416,13334,13335],{"class":3430}," field.primary_key:\n",[3416,13337,13338,13341],{"class":3418,"line":7615},[3416,13339,13340],{"class":3430},"                col += ",[3416,13342,13343],{"class":3500},"\" PRIMARY KEY AUTOINCREMENT\"\n",[3416,13345,13346,13348,13350,13353,13356,13358],{"class":3418,"line":7621},[3416,13347,11320],{"class":3634},[3416,13349,3687],{"class":3422},[3416,13351,13352],{"class":3430}," field.null ",[3416,13354,13355],{"class":3422},"and",[3416,13357,3687],{"class":3422},[3416,13359,13335],{"class":3430},[3416,13361,13362,13364],{"class":3418,"line":7626},[3416,13363,13340],{"class":3430},[3416,13365,13366],{"class":3500},"\" NOT NULL\"\n",[3416,13368,13369,13371,13373,13375,13377,13380,13382,13384,13386],{"class":3418,"line":7632},[3416,13370,11320],{"class":3634},[3416,13372,13001],{"class":3430},[3416,13374,4198],{"class":3422},[3416,13376,3687],{"class":3422},[3416,13378,13379],{"class":3430}," ... ",[3416,13381,13355],{"class":3422},[3416,13383,3687],{"class":3422},[3416,13385,11346],{"class":3440},[3416,13387,13388],{"class":3430},"(field.default):\n",[3416,13390,13391],{"class":3418,"line":7638},[3416,13392,13393],{"class":3430},"                default_val = (\n",[3416,13395,13396,13398,13401,13403,13406,13408,13411,13413,13415,13418,13420],{"class":3418,"line":7643},[3416,13397,11438],{"class":3422},[3416,13399,13400],{"class":3500},"\"'",[3416,13402,3723],{"class":3422},[3416,13404,13405],{"class":3430},"field.default",[3416,13407,3735],{"class":3422},[3416,13409,13410],{"class":3500},"'\"",[3416,13412,7501],{"class":3634},[3416,13414,3690],{"class":3440},[3416,13416,13417],{"class":3430},"(field.default, ",[3416,13419,3460],{"class":3426},[3416,13421,3504],{"class":3430},[3416,13423,13424,13427,13430,13433,13435,13437,13439,13441],{"class":3418,"line":7648},[3416,13425,13426],{"class":3634},"                    else",[3416,13428,13429],{"class":3426}," str",[3416,13431,13432],{"class":3430},"(field.default).upper() ",[3416,13434,7393],{"class":3634},[3416,13436,3690],{"class":3440},[3416,13438,13417],{"class":3430},[3416,13440,7241],{"class":3426},[3416,13442,3504],{"class":3430},[3416,13444,13445,13447,13449],{"class":3418,"line":7678},[3416,13446,13426],{"class":3634},[3416,13448,13429],{"class":3426},[3416,13450,13451],{"class":3430},"(field.default)\n",[3416,13453,13454],{"class":3418,"line":7686},[3416,13455,11462],{"class":3430},[3416,13457,13458,13460,13462,13465,13467,13470,13472],{"class":3418,"line":7691},[3416,13459,13340],{"class":3430},[3416,13461,3717],{"class":3422},[3416,13463,13464],{"class":3500},"\" DEFAULT ",[3416,13466,3723],{"class":3422},[3416,13468,13469],{"class":3430},"default_val",[3416,13471,3735],{"class":3422},[3416,13473,6676],{"class":3500},[3416,13475,13476],{"class":3418,"line":7712},[3416,13477,13478],{"class":3430},"            columns.append(col)\n",[3416,13480,13481,13484,13487,13489,13491],{"class":3418,"line":7727},[3416,13482,13483],{"class":3430},"        cols_sql = ",[3416,13485,13486],{"class":3500},"\",",[3416,13488,4588],{"class":4587},[3416,13490,3738],{"class":3500},[3416,13492,13493],{"class":3430},".join(columns)\n",[3416,13495,13496,13498,13500,13503,13505,13508,13510,13512,13514,13516,13519,13521,13523],{"class":3418,"line":7736},[3416,13497,3635],{"class":3634},[3416,13499,4513],{"class":3422},[3416,13501,13502],{"class":3500},"\"CREATE TABLE IF NOT EXISTS ",[3416,13504,3723],{"class":3422},[3416,13506,13507],{"class":3430},"table_name",[3416,13509,3735],{"class":3422},[3416,13511,4780],{"class":3500},[3416,13513,4588],{"class":4587},[3416,13515,3723],{"class":3422},[3416,13517,13518],{"class":3430},"cols_sql",[3416,13520,3735],{"class":3422},[3416,13522,4588],{"class":4587},[3416,13524,13525],{"class":3500},");\"\n",[3850,13527,13529],{"id":13528},"оголошення-моделей-і-демонстрація","Оголошення моделей і демонстрація",[3407,13531,13533],{"className":3409,"code":13532,"language":3411,"meta":3412,"style":3412},"# models.py\nfrom mini_orm import Model, IntegerField, CharField, FloatField, BooleanField\n\n\nclass User(Model):\n    \"\"\"Модель користувача — відповідає таблиці users у БД.\"\"\"\n    id         = IntegerField(primary_key=True, null=True)\n    username   = CharField(max_length=50)\n    email      = CharField(max_length=200)\n    age        = IntegerField(min_value=0, max_value=150, null=True)\n    is_active  = BooleanField(default=True)\n    score      = FloatField(default=0.0, null=True)\n\n\nclass Article(Model):\n    \"\"\"Модель статті — відповідає таблиці articles у БД.\"\"\"\n    id         = IntegerField(primary_key=True, null=True)\n    title      = CharField(max_length=255)\n    content    = CharField(max_length=10_000)\n    views      = IntegerField(default=0)\n    published  = BooleanField(default=False)\n\n\n# ─── Використання ─────────────────────────────────────────────────────────────\n\n# Створення екземплярів — повна валідація при ініціалізації\nuser = User(username=\"arakviel\", email=\"arakviel@example.com\", age=30)\nprint(user)\nprint(f\"to_dict: {user.to_dict()}\\n\")\n\n# Генерація SQL\nprint(User.generate_create_table_sql())\nprint()\nprint(Article.generate_create_table_sql())\n\n# Валідація при зміні полів\ntry:\n    user.age = 200          # перевищує max_value=150\nexcept ValueError as e:\n    print(f\"\\nValueError: {e}\")\n\ntry:\n    user.username = \"a\" * 100  # перевищує max_length=50\nexcept ValueError as e:\n    print(f\"ValueError: {e}\")\n\ntry:\n    user.is_active = 1      # int замість bool\nexcept TypeError as e:\n    print(f\"TypeError: {e}\")\n",[3398,13534,13535,13540,13552,13556,13560,13574,13579,13605,13619,13632,13661,13675,13696,13700,13704,13717,13722,13744,13757,13771,13784,13797,13801,13805,13810,13814,13819,13853,13860,13884,13888,13893,13900,13907,13914,13918,13923,13929,13939,13949,13973,13977,13983,13999,14009,14029,14033,14039,14049,14059],{"__ignoreMap":3412},[3416,13536,13537],{"class":3418,"line":3419},[3416,13538,13539],{"class":3516},"# models.py\n",[3416,13541,13542,13544,13547,13549],{"class":3418,"line":3434},[3416,13543,6564],{"class":3634},[3416,13545,13546],{"class":3430}," mini_orm ",[3416,13548,6570],{"class":3634},[3416,13550,13551],{"class":3430}," Model, IntegerField, CharField, FloatField, BooleanField\n",[3416,13553,13554],{"class":3418,"line":3466},[3416,13555,3491],{"emptyLinePlaceholder":3490},[3416,13557,13558],{"class":3418,"line":3475},[3416,13559,3491],{"emptyLinePlaceholder":3490},[3416,13561,13562,13564,13567,13569,13572],{"class":3418,"line":3487},[3416,13563,3423],{"class":3422},[3416,13565,13566],{"class":3426}," User",[3416,13568,3444],{"class":3430},[3416,13570,13571],{"class":3426},"Model",[3416,13573,3463],{"class":3430},[3416,13575,13576],{"class":3418,"line":3494},[3416,13577,13578],{"class":3500},"    \"\"\"Модель користувача — відповідає таблиці users у БД.\"\"\"\n",[3416,13580,13581,13584,13587,13590,13592,13594,13596,13599,13601,13603],{"class":3418,"line":3507},[3416,13582,13583],{"class":3440},"    id",[3416,13585,13586],{"class":3430},"         = IntegerField(",[3416,13588,13589],{"class":3447},"primary_key",[3416,13591,4177],{"class":3430},[3416,13593,7246],{"class":3422},[3416,13595,3451],{"class":3430},[3416,13597,13598],{"class":3447},"null",[3416,13600,4177],{"class":3430},[3416,13602,7246],{"class":3422},[3416,13604,3504],{"class":3430},[3416,13606,13607,13610,13612,13614,13617],{"class":3418,"line":3520},[3416,13608,13609],{"class":3430},"    username   = CharField(",[3416,13611,7661],{"class":3447},[3416,13613,4177],{"class":3430},[3416,13615,13616],{"class":3483},"50",[3416,13618,3504],{"class":3430},[3416,13620,13621,13624,13626,13628,13630],{"class":3418,"line":3532},[3416,13622,13623],{"class":3430},"    email      = CharField(",[3416,13625,7661],{"class":3447},[3416,13627,4177],{"class":3430},[3416,13629,8270],{"class":3483},[3416,13631,3504],{"class":3430},[3416,13633,13634,13637,13639,13641,13643,13645,13647,13649,13651,13653,13655,13657,13659],{"class":3418,"line":3648},[3416,13635,13636],{"class":3430},"    age        = IntegerField(",[3416,13638,11723],{"class":3447},[3416,13640,4177],{"class":3430},[3416,13642,6417],{"class":3483},[3416,13644,3451],{"class":3430},[3416,13646,11740],{"class":3447},[3416,13648,4177],{"class":3430},[3416,13650,8560],{"class":3483},[3416,13652,3451],{"class":3430},[3416,13654,13598],{"class":3447},[3416,13656,4177],{"class":3430},[3416,13658,7246],{"class":3422},[3416,13660,3504],{"class":3430},[3416,13662,13663,13666,13669,13671,13673],{"class":3418,"line":3654},[3416,13664,13665],{"class":3430},"    is_active  = BooleanField(",[3416,13667,13668],{"class":3447},"default",[3416,13670,4177],{"class":3430},[3416,13672,7246],{"class":3422},[3416,13674,3504],{"class":3430},[3416,13676,13677,13680,13682,13684,13686,13688,13690,13692,13694],{"class":3418,"line":3681},[3416,13678,13679],{"class":3430},"    score      = FloatField(",[3416,13681,13668],{"class":3447},[3416,13683,4177],{"class":3430},[3416,13685,8010],{"class":3483},[3416,13687,3451],{"class":3430},[3416,13689,13598],{"class":3447},[3416,13691,4177],{"class":3430},[3416,13693,7246],{"class":3422},[3416,13695,3504],{"class":3430},[3416,13697,13698],{"class":3418,"line":3706},[3416,13699,3491],{"emptyLinePlaceholder":3490},[3416,13701,13702],{"class":3418,"line":3743},[3416,13703,3491],{"emptyLinePlaceholder":3490},[3416,13705,13706,13708,13711,13713,13715],{"class":3418,"line":3756},[3416,13707,3423],{"class":3422},[3416,13709,13710],{"class":3426}," Article",[3416,13712,3444],{"class":3430},[3416,13714,13571],{"class":3426},[3416,13716,3463],{"class":3430},[3416,13718,13719],{"class":3418,"line":3782},[3416,13720,13721],{"class":3500},"    \"\"\"Модель статті — відповідає таблиці articles у БД.\"\"\"\n",[3416,13723,13724,13726,13728,13730,13732,13734,13736,13738,13740,13742],{"class":3418,"line":4157},[3416,13725,13583],{"class":3440},[3416,13727,13586],{"class":3430},[3416,13729,13589],{"class":3447},[3416,13731,4177],{"class":3430},[3416,13733,7246],{"class":3422},[3416,13735,3451],{"class":3430},[3416,13737,13598],{"class":3447},[3416,13739,4177],{"class":3430},[3416,13741,7246],{"class":3422},[3416,13743,3504],{"class":3430},[3416,13745,13746,13749,13751,13753,13755],{"class":3418,"line":4184},[3416,13747,13748],{"class":3430},"    title      = CharField(",[3416,13750,7661],{"class":3447},[3416,13752,4177],{"class":3430},[3416,13754,12101],{"class":3483},[3416,13756,3504],{"class":3430},[3416,13758,13759,13762,13764,13766,13769],{"class":3418,"line":4190},[3416,13760,13761],{"class":3430},"    content    = CharField(",[3416,13763,7661],{"class":3447},[3416,13765,4177],{"class":3430},[3416,13767,13768],{"class":3483},"10_000",[3416,13770,3504],{"class":3430},[3416,13772,13773,13776,13778,13780,13782],{"class":3418,"line":4206},[3416,13774,13775],{"class":3430},"    views      = IntegerField(",[3416,13777,13668],{"class":3447},[3416,13779,4177],{"class":3430},[3416,13781,6417],{"class":3483},[3416,13783,3504],{"class":3430},[3416,13785,13786,13789,13791,13793,13795],{"class":3418,"line":4217},[3416,13787,13788],{"class":3430},"    published  = BooleanField(",[3416,13790,13668],{"class":3447},[3416,13792,4177],{"class":3430},[3416,13794,11030],{"class":3422},[3416,13796,3504],{"class":3430},[3416,13798,13799],{"class":3418,"line":4238},[3416,13800,3491],{"emptyLinePlaceholder":3490},[3416,13802,13803],{"class":3418,"line":4286},[3416,13804,3491],{"emptyLinePlaceholder":3490},[3416,13806,13807],{"class":3418,"line":4294},[3416,13808,13809],{"class":3516},"# ─── Використання ─────────────────────────────────────────────────────────────\n",[3416,13811,13812],{"class":3418,"line":4299},[3416,13813,3491],{"emptyLinePlaceholder":3490},[3416,13815,13816],{"class":3418,"line":4325},[3416,13817,13818],{"class":3516},"# Створення екземплярів — повна валідація при ініціалізації\n",[3416,13820,13821,13824,13827,13829,13832,13834,13837,13839,13842,13844,13846,13848,13851],{"class":3418,"line":4368},[3416,13822,13823],{"class":3430},"user = User(",[3416,13825,13826],{"class":3447},"username",[3416,13828,4177],{"class":3430},[3416,13830,13831],{"class":3500},"\"arakviel\"",[3416,13833,3451],{"class":3430},[3416,13835,13836],{"class":3447},"email",[3416,13838,4177],{"class":3430},[3416,13840,13841],{"class":3500},"\"arakviel@example.com\"",[3416,13843,3451],{"class":3430},[3416,13845,4451],{"class":3447},[3416,13847,4177],{"class":3430},[3416,13849,13850],{"class":3483},"30",[3416,13852,3504],{"class":3430},[3416,13854,13855,13857],{"class":3418,"line":4381},[3416,13856,4570],{"class":3440},[3416,13858,13859],{"class":3430},"(user)\n",[3416,13861,13862,13864,13866,13868,13871,13873,13876,13878,13880,13882],{"class":3418,"line":4386},[3416,13863,4570],{"class":3440},[3416,13865,3444],{"class":3430},[3416,13867,3717],{"class":3422},[3416,13869,13870],{"class":3500},"\"to_dict: ",[3416,13872,3723],{"class":3422},[3416,13874,13875],{"class":3430},"user.to_dict()",[3416,13877,3735],{"class":3422},[3416,13879,4588],{"class":4587},[3416,13881,3738],{"class":3500},[3416,13883,3504],{"class":3430},[3416,13885,13886],{"class":3418,"line":4391},[3416,13887,3491],{"emptyLinePlaceholder":3490},[3416,13889,13890],{"class":3418,"line":4401},[3416,13891,13892],{"class":3516},"# Генерація SQL\n",[3416,13894,13895,13897],{"class":3418,"line":4407},[3416,13896,4570],{"class":3440},[3416,13898,13899],{"class":3430},"(User.generate_create_table_sql())\n",[3416,13901,13902,13904],{"class":3418,"line":4413},[3416,13903,4570],{"class":3440},[3416,13905,13906],{"class":3430},"()\n",[3416,13908,13909,13911],{"class":3418,"line":4419},[3416,13910,4570],{"class":3440},[3416,13912,13913],{"class":3430},"(Article.generate_create_table_sql())\n",[3416,13915,13916],{"class":3418,"line":4425},[3416,13917,3491],{"emptyLinePlaceholder":3490},[3416,13919,13920],{"class":3418,"line":4430},[3416,13921,13922],{"class":3516},"# Валідація при зміні полів\n",[3416,13924,13925,13927],{"class":3418,"line":4464},[3416,13926,8493],{"class":3634},[3416,13928,3431],{"class":3430},[3416,13930,13931,13934,13936],{"class":3418,"line":4475},[3416,13932,13933],{"class":3430},"    user.age = ",[3416,13935,8270],{"class":3483},[3416,13937,13938],{"class":3516},"          # перевищує max_value=150\n",[3416,13940,13941,13943,13945,13947],{"class":3418,"line":4485},[3416,13942,8511],{"class":3634},[3416,13944,3761],{"class":3426},[3416,13946,8516],{"class":3634},[3416,13948,8519],{"class":3430},[3416,13950,13951,13953,13955,13957,13959,13961,13963,13965,13967,13969,13971],{"class":3418,"line":4490},[3416,13952,8524],{"class":3440},[3416,13954,3444],{"class":3430},[3416,13956,3717],{"class":3422},[3416,13958,3738],{"class":3500},[3416,13960,4588],{"class":4587},[3416,13962,8727],{"class":3500},[3416,13964,3723],{"class":3422},[3416,13966,8536],{"class":3430},[3416,13968,3735],{"class":3422},[3416,13970,3738],{"class":3500},[3416,13972,3504],{"class":3430},[3416,13974,13975],{"class":3418,"line":4508},[3416,13976,3491],{"emptyLinePlaceholder":3490},[3416,13978,13979,13981],{"class":3418,"line":4541},[3416,13980,8493],{"class":3634},[3416,13982,3431],{"class":3430},[3416,13984,13985,13988,13991,13994,13996],{"class":3418,"line":4546},[3416,13986,13987],{"class":3430},"    user.username = ",[3416,13989,13990],{"class":3500},"\"a\"",[3416,13992,13993],{"class":3430}," * ",[3416,13995,6484],{"class":3483},[3416,13997,13998],{"class":3516},"  # перевищує max_length=50\n",[3416,14000,14001,14003,14005,14007],{"class":3418,"line":4551},[3416,14002,8511],{"class":3634},[3416,14004,3761],{"class":3426},[3416,14006,8516],{"class":3634},[3416,14008,8519],{"class":3430},[3416,14010,14011,14013,14015,14017,14019,14021,14023,14025,14027],{"class":3418,"line":4567},[3416,14012,8524],{"class":3440},[3416,14014,3444],{"class":3430},[3416,14016,3717],{"class":3422},[3416,14018,8531],{"class":3500},[3416,14020,3723],{"class":3422},[3416,14022,8536],{"class":3430},[3416,14024,3735],{"class":3422},[3416,14026,3738],{"class":3500},[3416,14028,3504],{"class":3430},[3416,14030,14031],{"class":3418,"line":4576},[3416,14032,3491],{"emptyLinePlaceholder":3490},[3416,14034,14035,14037],{"class":3418,"line":4608},[3416,14036,8493],{"class":3634},[3416,14038,3431],{"class":3430},[3416,14040,14041,14044,14046],{"class":3418,"line":4613},[3416,14042,14043],{"class":3430},"    user.is_active = ",[3416,14045,9651],{"class":3483},[3416,14047,14048],{"class":3516},"      # int замість bool\n",[3416,14050,14051,14053,14055,14057],{"class":3418,"line":4619},[3416,14052,8511],{"class":3634},[3416,14054,3712],{"class":3426},[3416,14056,8516],{"class":3634},[3416,14058,8519],{"class":3430},[3416,14060,14061,14063,14065,14067,14069,14071,14073,14075,14077],{"class":3418,"line":5772},[3416,14062,8524],{"class":3440},[3416,14064,3444],{"class":3430},[3416,14066,3717],{"class":3422},[3416,14068,8686],{"class":3500},[3416,14070,3723],{"class":3422},[3416,14072,8536],{"class":3430},[3416,14074,3735],{"class":3422},[3416,14076,3738],{"class":3500},[3416,14078,3504],{"class":3430},[4645,14080,14082,14090,14119,14127,14130,14137,14146,14154,14162,14170,14178,14186,14193,14196,14203,14210],{"title":14081},"python models.py",[4649,14083,14085,4657,14088],{"className":14084},[3418],[3416,14086,4656],{"className":14087},[4655],[3838,14089,14081],{},[4649,14091,14093,14094,14097,14098,14102,14103,14107,14108,14111,14112,14115,14116,5221],{"className":14092},[3418],"User(id=",[3416,14095,3676],{"className":14096},[4721],", username=",[3416,14099,14101],{"className":14100},[4683],"'arakviel'",", email=",[3416,14104,14106],{"className":14105},[4683],"'arakviel@example.com'",", age=",[3416,14109,13850],{"className":14110},[4683],", is_active=",[3416,14113,7246],{"className":14114},[4683],", score=",[3416,14117,8010],{"className":14118},[4683],[4649,14120,14122,14123],{"className":14121},[3418],"to_dict: ",[3416,14124,14126],{"className":14125},[4667],"{'id': None, 'username': 'arakviel', 'email': 'arakviel@example.com', 'age': 30, 'is_active': True, 'score': 0.0}",[4649,14128],{"className":14129},[3418],[4649,14131,14133],{"className":14132},[3418],[3416,14134,14136],{"className":14135},[4710],"CREATE TABLE IF NOT EXISTS users (",[4649,14138,14140,14141,14145],{"className":14139},[3418],"    id ",[3416,14142,14144],{"className":14143},[4667],"INTEGER PRIMARY KEY AUTOINCREMENT",",",[4649,14147,14149,14150,14145],{"className":14148},[3418],"    username ",[3416,14151,14153],{"className":14152},[4667],"VARCHAR(50) NOT NULL",[4649,14155,14157,14158,14145],{"className":14156},[3418],"    email ",[3416,14159,14161],{"className":14160},[4667],"VARCHAR(200) NOT NULL",[4649,14163,14165,14166,14145],{"className":14164},[3418],"    age ",[3416,14167,14169],{"className":14168},[4667],"INTEGER",[4649,14171,14173,14174,14145],{"className":14172},[3418],"    is_active ",[3416,14175,14177],{"className":14176},[4667],"BOOLEAN NOT NULL DEFAULT TRUE",[4649,14179,14181,14182],{"className":14180},[3418],"    score ",[3416,14183,14185],{"className":14184},[4667],"REAL DEFAULT 0.0",[4649,14187,14189],{"className":14188},[3418],[3416,14190,14192],{"className":14191},[4710],");",[4649,14194],{"className":14195},[3418],[4649,14197,8727,14199],{"className":14198},[3418],[3416,14200,14202],{"className":14201},[8731],"Поле 'age': 200 > максимум 150",[4649,14204,8727,14206],{"className":14205},[3418],[3416,14207,14209],{"className":14208},[8731],"Поле 'username': рядок завдовжки 100 перевищує max_length=50",[4649,14211,8750,14213],{"className":14212},[3418],[3416,14214,14216],{"className":14215},[8731],"Поле 'is_active' очікує bool, отримано 'int'",[3850,14218,14220],{"id":14219},"що-стоїть-за-django-orm","Що стоїть за Django ORM",[3394,14222,14223],{},"Ця реалізація — спрощена модель того, як насправді влаштований Django ORM. Реальний Django робить значно більше:",[3896,14225,14226,14233,14246,14256],{},[3899,14227,14228,14229,14232],{},"Метаклас ",[3398,14230,14231],{},"ModelBase"," збирає поля, валідатори, індекси та зовнішні ключі",[3899,14234,14235,14236,3451,14239,14241,14242,14245],{},"Кожне поле (",[3398,14237,14238],{},"CharField",[3398,14240,5258],{}," тощо) є дескриптором, що підтримує ",[3398,14243,14244],{},"contribute_to_class()"," для реєстрації у моделі",[3899,14247,14248,14251,14252,14255],{},[3398,14249,14250],{},"QuerySet"," та менеджери також реалізовані як дескриптори (",[3398,14253,14254],{},"objects = Manager()"," у кожній моделі)",[3899,14257,14258,14261],{},[3398,14259,14260],{},"ForeignKey"," через дескриптор повертає пов'язаний об'єкт, виконуючи SQL-запит при першому зверненні — ледаче завантаження (lazy loading)",[3394,14263,14264],{},"Тобто весь Django ORM — це, по суті, велика і складна система дескрипторів і метакласів, побудована на тих самих принципах, що ми щойно реалізували вручну.",[3843,14266],{},[3389,14268,14270],{"id":14269},"підсумок-коли-і-навіщо-використовувати-дескриптори","Підсумок: коли і навіщо використовувати дескриптори",[3394,14272,14273],{},"Дескриптори — це інструмент конкретних задач. Вони не є «кращим способом» написання будь-якого коду, але незамінні у специфічних сценаріях:",[3809,14275,14276,14290,14300,14305],{},[3812,14277,14280,14281,3451,14284,3451,14287,3986],{"icon":14278,"title":14279},"i-heroicons-shield-check","✅ Перевикористовувана валідація","Якщо одна і та сама логіка перевірки потрібна в кількох класах — виносьте її в дескриптор. Класичний приклад: ",[3398,14282,14283],{},"RangedField",[3398,14285,14286],{},"NonEmptyString",[3398,14288,14289],{},"EmailField",[3812,14291,14293,14294,14296,14297,14299],{"icon":8861,"title":14292},"✅ Ледаче кешування","Атрибут, що дорого обчислюється, але потрібен не завжди — ідеальний кандидат для ",[3398,14295,8876],{},". Використовуйте ",[3398,14298,10305],{}," у production.",[3812,14301,14304],{"icon":14302,"title":14303},"i-heroicons-code-bracket-square","✅ Декларативні API","ORM-поля, поля форм, конфігурація через атрибути класу — будь-яка декларативна система, де «оголошення атрибуту = опис поведінки».",[3812,14306,14309,14310,14312],{"icon":14307,"title":14308},"i-heroicons-x-mark","❌ Проста валідація в одному класі","Якщо перевірка потрібна лише для одного атрибуту одного класу — ",[3398,14311,3549],{}," достатньо. Дескриптор тут надмірний.",[3850,14314,14316],{"id":14315},"ключові-принципи-які-варто-памятати","Ключові принципи, які варто пам'ятати",[5185,14318,14319,14329],{},[5188,14320,14321],{},[5191,14322,14323,14326],{},[5194,14324,14325],{},"Принцип",[5194,14327,14328],{},"Деталь",[5204,14330,14331,14343,14358,14372,14381,14395,14408],{},[5191,14332,14333,14336],{},[5209,14334,14335],{},"Розміщення",[5209,14337,14338,14339,14342],{},"Дескриптор — атрибут ",[3838,14340,14341],{},"класу",", не екземпляра",[5191,14344,14345,14348],{},[5209,14346,14347],{},"Data vs Non-data",[5209,14349,14350,14352,14353,14355,14356,5221],{},[3398,14351,3865],{}," \u002F ",[3398,14354,3869],{}," → data (пріоритет вищий за ",[3398,14357,4758],{},[5191,14359,14360,14363],{},[5209,14361,14362],{},"Зберігання значень",[5209,14364,14365,14366,14368,14369],{},"У ",[3398,14367,4894],{}," під «приватним» іменем або через ",[3398,14370,14371],{},"_name",[5191,14373,14374,14378],{},[5209,14375,14376],{},[3398,14377,4799],{},[5209,14379,14380],{},"З Python 3.6 — отримуємо ім'я атрибуту автоматично, без конструктора",[5191,14382,14383,14389],{},[5209,14384,14385,4771,14387],{},[3398,14386,4770],{},[3398,14388,3862],{},[5209,14390,14391,14392,14394],{},"Доступ через клас → повертаємо ",[3398,14393,3448],{}," (сам дескриптор)",[5191,14396,14397,14400],{},[5209,14398,14399],{},"Порядок пошуку",[5209,14401,14402,14403,14405,14406],{},"Data descriptor → ",[3398,14404,4758],{}," → Non-data descriptor → ",[3398,14407,4939],{},[5191,14409,14410,14413],{},[5209,14411,14412],{},"Вбудовані приклади",[5209,14414,14415,3451,14417,3451,14420,14423],{},[3398,14416,6242],{},[3398,14418,14419],{},"classmethod",[3398,14421,14422],{},"staticmethod",", звичайні функції",[14425,14426,14427],"style",{},"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 .sHH4Y, html code.shiki .sHH4Y{--shiki-light:#000000;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}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 .sJj4R, html code.shiki .sJj4R{--shiki-light:#098658;--shiki-default:#B5CEA8;--shiki-dark:#B5CEA8}html pre.shiki code .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}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 .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}",{"title":3412,"searchDepth":3434,"depth":3434,"links":14429},[14430,14431,14436,14450,14455,14462,14468],{"id":3391,"depth":3434,"text":3392},{"id":3847,"depth":3434,"text":3848,"children":14432},[14433,14434,14435],{"id":3852,"depth":3466,"text":3853},{"id":3930,"depth":3466,"text":3931},{"id":4014,"depth":3466,"text":4015},{"id":4814,"depth":3434,"text":4815,"children":14437},[14438,14440,14441,14443,14444,14446,14447,14448],{"id":4818,"depth":3466,"text":14439},"Як Python вирішує obj.attr: повна картина",{"id":4847,"depth":3466,"text":4848},{"id":4884,"depth":3466,"text":14442},"Крок 2: Пошук у __dict__ екземпляра",{"id":4909,"depth":3466,"text":4910},{"id":4935,"depth":3466,"text":14445},"Крок 4: __getattr__ як запасний варіант",{"id":5179,"depth":3466,"text":5180},{"id":5932,"depth":3466,"text":5933},{"id":6209,"depth":3466,"text":14449},"Підтвердження: @property як data descriptor",{"id":6538,"depth":3434,"text":6539,"children":14451},[14452,14453,14454],{"id":6542,"depth":3466,"text":6543},{"id":7911,"depth":3466,"text":7912},{"id":8757,"depth":3466,"text":8758},{"id":8821,"depth":3434,"text":8822,"children":14456},[14457,14458,14459,14461],{"id":8825,"depth":3466,"text":8826},{"id":8869,"depth":3466,"text":8870},{"id":10301,"depth":3466,"text":14460},"Порівняння з functools.cached_property",{"id":10742,"depth":3466,"text":10743},{"id":10879,"depth":3434,"text":10880,"children":14463},[14464,14465,14466,14467],{"id":10883,"depth":3466,"text":10884},{"id":12624,"depth":3466,"text":12625},{"id":13528,"depth":3466,"text":13529},{"id":14219,"depth":3466,"text":14220},{"id":14269,"depth":3434,"text":14270,"children":14469},[14470],{"id":14315,"depth":3466,"text":14316},"Глибоке дослідження протоколу дескрипторів Python — методів __get__, __set__, __delete__ та __set_name__. Алгоритм пошуку атрибутів, різниця між data та non-data дескрипторами, а також практичні сценарії використання — від валідації полів до реалізації ORM і ледачого обчислення.","md",null,{},{"title":2581,"description":14471},"D_FDwf5ZAiB0_VvcVBjiPO6q2Uc5MCH1FkF8KbB5gJc",[14478,14480],{"title":2577,"path":2578,"stem":2579,"description":14479,"children":-1},"Глибокий розбір декораторів у Python — від @staticmethod і @classmethod до декораторів класів і callable-екземплярів. Як декоратори трансформують методи, зберігають стан між викликами та дозволяють будувати гнучку авторизацію й кешування без зміни основного коду.",{"title":2585,"path":2586,"stem":2587,"description":14481,"children":-1},"Глибокий розбір динамічної природи класів у Python. Використання type() для створення класів на льоту, створення та налаштування власних метакласів, життєвий цикл __new__ та __init__, а також сучасна альтернатива у вигляді __init_subclass__ (PEP 487).",1783248144573]