Развёрнутое пояснение:
1. grid = [[0] * 3] * 3 создаёт один внутренний список [0, 0, 0] и трижды копирует ссылку на него, так что все три строки указывают на один объект.
2. grid[0][0] = 1 через первую ссылку изменяет этот общий список, поэтому у любой строки значение в позиции 0 становится равно 1.
3. print(grid) печатает один и тот же изменённый список три раза: [[1, 0, 0], [1, 0, 0], [1, 0, 0]].
Почему это важно
Такая ловушка встречается при инициализации DP-таблиц, матриц смежности или игровых полей. Если написать [[0] * n] * m, любая запись в ячейку размножается по всем строкам, и алгоритм начинает работать с испорченными данными. Правильный способ — [[0] * n for _ in range(m)]: здесь каждая строка создаётся отдельным выражением и живёт своим объектом.
💻 Это различие между копией ссылки и копией объекта — базовая часть работы с массивами и алгоритмами на них. Такие паттерны, сложность операций и подготовку к алгоритмическим собеседованиям разбирают в Практикуме PRO на курсе Алгоритмы и структуры данных : 4 месяца, 100+ задач и пробное интервью.
Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 7704282033, erid: 2W5zFK6uaWV
1. grid = [[0] * 3] * 3 создаёт один внутренний список [0, 0, 0] и трижды копирует ссылку на него, так что все три строки указывают на один объект.
2. grid[0][0] = 1 через первую ссылку изменяет этот общий список, поэтому у любой строки значение в позиции 0 становится равно 1.
3. print(grid) печатает один и тот же изменённый список три раза: [[1, 0, 0], [1, 0, 0], [1, 0, 0]].
Почему это важно
Такая ловушка встречается при инициализации DP-таблиц, матриц смежности или игровых полей. Если написать [[0] * n] * m, любая запись в ячейку размножается по всем строкам, и алгоритм начинает работать с испорченными данными. Правильный способ — [[0] * n for _ in range(m)]: здесь каждая строка создаётся отдельным выражением и живёт своим объектом.
Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 7704282033, erid: 2W5zFK6uaWV
Please open Telegram to view this post
VIEW IN TELEGRAM
✍2
Python: задачки и вопросы
Развёрнутое пояснение:
1. Создаётся u с id=1; его __hash__ возвращает 1.
2. Словарь d помещает пару u: 'a' в корзину для хэша 1.
3. u.id меняется на 2; сам объект u всё ещё лежит в корзине 1, потому что словарь не пересчитывает хэш.
4. d.get(User(2)) вычисляет хэш нового объекта — 2 — и ищет в корзине 2.
5. В корзине 2 ничего нет, поэтому возвращается None.
Почему это важно: ключи dict и элементы set должны быть неизменяемыми относительно величины, участвующей в __hash__. Если объект-ключ мутирует, он остаётся в старой корзине, и поиск по новому хэшу его не найдёт. Это приводит к утечкам в кэшах, потере данных в индексах и нестабильным тестам при использовании изменяемых сущностей в качестве ключей.
2. Словарь d помещает пару u: 'a' в корзину для хэша 1.
3.
4. d.get(User(2)) вычисляет хэш нового объекта — 2 — и ищет в корзине 2.
5. В корзине 2 ничего нет, поэтому возвращается None.
Почему это важно: ключи dict и элементы set должны быть неизменяемыми относительно величины, участвующей в __hash__. Если объект-ключ мутирует, он остаётся в старой корзине, и поиск по новому хэшу его не найдёт. Это приводит к утечкам в кэшах, потере данных в индексах и нестабильным тестам при использовании изменяемых сущностей в качестве ключей.
✍2
Python: задачки и вопросы
Развёрнутое пояснение:
1. Определяется класс Field с методами __set__ и __get__, что делает его дескриптором данных.
2. Класс Record получает атрибут класса value, равный экземпляру Field.
3. Создаётся r = Record().
4. Присваивание r.value = 3 вызывает дескрипторный метод __set__, который записывает в r._val значение 3 * 10, то есть 30.
5. Прямая запись r.__dict__["value"] = 9 помещает 9 в словарь экземпляра, но не вызывает дескриптор и не заменяет атрибут класса.
6. При чтении r.value правило поиска атрибутов в Python сначала проверяет дескриптор данных в классе; он находит value = Field() и вызывает его __get__, возвращающий r._val, равное 30.
7. Значение 9 из __dict__ остаётся недоступным через обычный доступ r.value, пока дескриптор с __set__ существует.
Почему это важно: В ORM, валидаторах и property с setter часто скрывают хранение значения за дескрипторами. Ошибка возникает, когда разработчик пишет obj.__dict__["field"] =... или сериализатор заполняет словарь напрямую, ожидая, что это эквивалентно obj.field =.... В действительности дескриптор данных переопределяет чтение, поэтому тесты могут показать одно значение, а обычный доступ — другое. Понимание приоритета дескрипторов помогает избежать рассинхронизации между внутренним хранилищем и публичным интерфейсом.
2. Класс Record получает атрибут класса value, равный экземпляру Field.
3. Создаётся r = Record().
4. Присваивание r.value = 3 вызывает дескрипторный метод __set__, который записывает в r._val значение 3 * 10, то есть 30.
5. Прямая запись r.__dict__["value"] = 9 помещает 9 в словарь экземпляра, но не вызывает дескриптор и не заменяет атрибут класса.
6. При чтении r.value правило поиска атрибутов в Python сначала проверяет дескриптор данных в классе; он находит value = Field() и вызывает его __get__, возвращающий r._val, равное 30.
7. Значение 9 из __dict__ остаётся недоступным через обычный доступ r.value, пока дескриптор с __set__ существует.
Почему это важно: В ORM, валидаторах и property с setter часто скрывают хранение значения за дескрипторами. Ошибка возникает, когда разработчик пишет obj.__dict__["field"] =... или сериализатор заполняет словарь напрямую, ожидая, что это эквивалентно obj.field =.... В действительности дескриптор данных переопределяет чтение, поэтому тесты могут показать одно значение, а обычный доступ — другое. Понимание приоритета дескрипторов помогает избежать рассинхронизации между внутренним хранилищем и публичным интерфейсом.
✍2
Развёрнутое пояснение:
1. Создаём множество с тремя объектами: 1 (int), 1.0 (float), True (bool).
2. Python сначала сравнивает хэши: hash(1), hash(1.0) и hash(True) равны.
3. При совпадении хэшей множество проверяет равенство через ==: 1 == 1.0 == True возвращает True.
4. Все три значения признаются дубликатами, и в множестве остаётся только первый вставленный элемент — 1.
5. print(len(data)) выводит 1.
Почему это важно
В ETL-пайплайнах такое поведение приводит к потере строк: дедупликация ключей int/float/bool или группировка по ним сворачивает разные сущности в одну. Поэтому в ключах явно приводят типы и не смешивают bool с числами.
💻 ETL-автоматизация — это только один из этапов разработки архитектуры данных. Если хотите системно разобраться в этой сфере, посмотрите программу Инженер данных от Практикума PRO. Вы сделаете 8 проектов, в рамках которых научитесь обрабатывать данные разными инструментами, проектировать пайплайны, создавать витрины и хранилища. Обратите внимание, что курс на полгода и он не для начинающих: вам понадобятся уверенные знания Python и SQL.
Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 7704282033, erid: 2W5zFJBoozq
2. Python сначала сравнивает хэши: hash(1), hash(1.0) и hash(True) равны.
3. При совпадении хэшей множество проверяет равенство через ==: 1 == 1.0 == True возвращает True.
4. Все три значения признаются дубликатами, и в множестве остаётся только первый вставленный элемент — 1.
5. print(len(data)) выводит 1.
Почему это важно
В ETL-пайплайнах такое поведение приводит к потере строк: дедупликация ключей int/float/bool или группировка по ним сворачивает разные сущности в одну. Поэтому в ключах явно приводят типы и не смешивают bool с числами.
💻 ETL-автоматизация — это только один из этапов разработки архитектуры данных. Если хотите системно разобраться в этой сфере, посмотрите
Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 7704282033, erid: 2W5zFJBoozq
✍2