Типы данных
Списки, кортежи, строки, множества и словари — изменяемость, срезы, генераторы коллекций.
56 вопросов
JuniorТеорияОчень частоВ чём разница между list и tuple в Python?
В чём разница между list и tuple в Python?
list изменяемый — можно append, insert или удалять; tuple неизменяемый после создания. Различаются и в CPython: list выделяет память с запасом, поэтому append амортизированно O(1), а tuple фиксированного размера, компактнее и кэшируется через free list.
Типичные ошибки
- ✗Считать
listиtupleодинаковыми под капотом — CPython хранит и оптимизирует их по-разному - ✗Думать, что
tupleполностью неизменяем, даже когда содержит изменяемый элемент вроде списка - ✗Полагать, что кортежи всегда быстрее — выигрыш в памяти и хешируемости, а не в скорости вообще
Уточняющие вопросы
- →Можно ли использовать
tupleкак ключdict? В чём подвох с вложенными изменяемыми? - →Почему
listвыделяет память с запасом, а не растёт на одну ячейку заappend?
JuniorТеорияОчень частоЧто такое отображение (mapping) в Python и какой канонический пример?
Что такое отображение (mapping) в Python и какой канонический пример?
Отображение даёт доступ к значениям по ключам через методы вроде get, keys, values, items. Канонический пример — dict; стандартная библиотека добавляет defaultdict, OrderedDict, Counter. Ключи должны быть хешируемыми, значения — любыми.
Типичные ошибки
- ✗Думать, что значения
dictдолжны быть хешируемыми — только ключи - ✗Считать, что
dictхранит ключи отсортированными; с 3.7 он хранит порядок вставки, а не сортировку - ✗Путать доступ по ключу через хеш с позиционным доступом по индексу
Уточняющие вопросы
- →В чём разница между
d[key]иd.get(key)? - →Что
defaultdictдобавляет к обычномуdict?
JuniorТеорияОчень частоЧто такое set и чем frozenset от него отличается?
Что такое set и чем frozenset от него отличается?
set — неупорядоченная коллекция уникальных хешируемых элементов без индексов и срезов, с быстрой проверкой in и операциями |, & и -. set изменяемый (add, remove); frozenset — его неизменяемый хешируемый вариант, годный как ключ dict.
Типичные ошибки
- ✗Ждать, что множество сохранит порядок вставки или поддержит индексацию: оно неупорядочено, поэтому
s[0]и срезы вызываютTypeError. - ✗Пытаться положить
listилиdictв множество: элементы должны быть хешируемыми; из двух видов множеств только неизменяемыйfrozensetсам годится как элемент множества или ключdict.
Уточняющие вопросы
- →Почему элементы множества должны быть хешируемыми?
- →Когда выбрать
frozensetвместоset?
JuniorТеорияОчень частоКак работает срез s[start:stop:step] у последовательности?
Как работает срез s[start:stop:step] у последовательности?
Возвращает новую подпоследовательность от start (включительно) до stop (не включая), беря каждый step-й элемент. Все три необязательны; отрицательные индексы отсчитываются с конца, а s[:] делает поверхностную копию. Выход за границы обрезается, а не падает с ошибкой.
Типичные ошибки
- ✗Ждать, что
stopвключается - ✗Считать, что выход за границы бросает ошибку, а не обрезается
- ✗Думать, что
s[:]— глубокая копия, а не поверхностная
Уточняющие вопросы
- →Что выдаёт отрицательный шаг вроде
s[::-1]и почему? - →Чем присваивание срезу
s[1:3] = [...]отличается от присваивания одному элементу?
JuniorТеорияОчень частоМожно ли изменить отдельный символ в строке Python на месте?
Можно ли изменить отдельный символ в строке Python на месте?
Нет — str неизменяемый. s[0] = 'x' бросает TypeError; любая «правка» вроде replace или + создаёт новый строковый объект и переназначает имя, оставляя исходную строку нетронутой. Именно неизменяемость делает строки хешируемыми и пригодными как ключи dict.
Типичные ошибки
- ✗Пробовать
s[i] = cи ждать, что сработает как у списка - ✗Думать, что
s.replace(...)меняетsна месте, а не возвращает новую строку - ✗Собирать строки через
+=в плотном цикле, не зная, что каждый шаг выделяет новый объект
Уточняющие вопросы
- →Почему многократный
+=для строки в цикле плохо масштабируется и как это исправить? - →Как неизменяемость строк позволяет CPython безопасно интернировать и кэшировать литералы?
MiddleТеорияОчень частоЧто может и не может быть ключом dict и почему?
Что может и не может быть ключом dict и почему?
Ключом может быть любой хешируемый объект: int, str, хешируемый tuple, даже функция или модуль. list, dict, set нельзя — они изменяемы и нехешируемы. Хеш tuple рекурсивен, поэтому tuple с dict внутри тоже нехешируем.
Типичные ошибки
- ✗Думать, что любой
tuple— валидный ключ —tupleсlist/dictвнутри нехешируем - ✗Считать, что любая коллекция запрещена как ключ —
frozensetхешируем и работает - ✗Путать неизменяемость с хешируемостью в требовании к ключу
Уточняющие вопросы
- →Почему
(1, [2])— невалидный ключdict? - →Как добавление классу
__eq__влияет на его пригодность как ключа?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
zero
Открыть задачу →Типичные ошибки
- ✗Ждать
minus, будтоelseзавершает выражение: послеelseстоит ещё одно"minus" if n < 0 else "zero", а0 < 0ложно. - ✗Ждать
plus, будто ноль считается положительным:0 > 0ложно, поэтому первое значение пропускается и проверяетсяn < 0.
Уточняющие вопросы
- →Как записать тот же выбор через
if,elifиelse? - →Что выведет программа при
n = -3?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
0 None False
Открыть задачу →Типичные ошибки
- ✗Ждать
5для"pear": значение по умолчанию берётся, только если ключа нет, а"pear"есть со значением0, поэтомуgetвозвращает0. - ✗Ждать
KeyErrorдля"plum":getне вызывает ошибку на отсутствующем ключе и без значения по умолчанию возвращаетNone. Ошибку даёт толькоstock["plum"].
Уточняющие вопросы
- →Что сделает вместо этого
print(stock["plum"])? - →Как прибавить единицу к
stock["pear"]с помощьюget?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
2 avocado
Открыть задачу →Типичные ошибки
- ✗Ждать
apple, будто побеждает первый ключ: каждая новая пара с ключом"a"заменяет старое значение, поэтому остаётся последнее такое слово,avocado. - ✗Ждать
3записи: словарь хранит каждый ключ один раз, а уappleиavocadoобщий ключ"a", поэтому остаётся2ключа, и ошибки нет.
Уточняющие вопросы
- →Как сохранить для каждой буквы первое слово, а не последнее?
- →Что получится из
{len(w) for w in words}?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
3.5 3 -4 2
Открыть задачу →Типичные ошибки
- ✗Ждать, что
-7 // 2— это-3, будто//отбрасывает дробь://округляет вниз, к минус бесконечности, поэтому-3.5становится-4. - ✗Ждать, что
-7 % 3— это-1: в Python знак остатка совпадает со знаком делителя, поэтому он2, ведь-7 // 3— это-3, а-3 * 3 + 2— это-7.
Уточняющие вопросы
- →Чему равно
7 % -3и почему? - →Почему
6 / 2печатает3.0, а не3?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
C
Открыть задачу →Типичные ошибки
- ✗Ждать
A, будто Python выбирает самое точное условие: он проверяет их сверху вниз, и первым истинноscore > 50, поэтому печатается толькоC. - ✗Ждать и
C, иA: после того как выполнилась одна ветка цепочкиif/elif, остальные пропускаются, даже если их условия тоже истинны.
Уточняющие вопросы
- →Как переставить условия, чтобы при
85печаталосьA? - →Что будет напечатано, если заменить
elifна второйif?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
False True
Открыть задачу →Типичные ошибки
- ✗Ждать, что
0.1 + 0.2в точности равно0.3: оба числа хранятся в двоичном виде с крошечной погрешностью, и сумма равна0.30000000000000004. - ✗Считать, что
roundне поможет:round(total, 2)даёт ближайшее к0.30числоfloat, то же, что даёт литерал0.3, поэтому они равны.
Уточняющие вопросы
- →Что покажет
print(0.1 + 0.2)? - →Как сравнить два вычисленных числа
floatбезround?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
3x3.14=9.4
Открыть задачу →Типичные ошибки
- ✗Ждать
3.1для{price:.2f}, будто2— это все цифры:.2fозначает две цифры после точки, поэтому3.14159выводится как3.14. - ✗Ждать
9.0, будтоint, умноженный наfloat, теряет дробную часть:qty * priceпримерно равно9.425, а.1fоставляет один знак —9.4.
Уточняющие вопросы
- →Меняет ли
{price:.2f}значение, которое хранится вprice? - →Как напечатать в f-строке символ
{?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
2 8 3.0
Открыть задачу →Типичные ошибки
- ✗Ждать, что
int(2.9)округлит до3:int()отбрасывает дробную часть, поэтомуint(2.9)— это2, а не ближайшее целое3. - ✗Ждать
71, будто+ 1дописывает цифру1после7:int("7")— это число7, и число плюс1даёт8.
Уточняющие вопросы
- →Что делает
int("2.5")и как получить из этой строки2? - →Что выведет
print(int(-2.9))?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
4 3
Открыть задачу →Типичные ошибки
- ✗Ждать
3 3, будтоb = aкопирует список: это второе имя для того же списка, поэтомуb.append(4)добавляет элемент и вa. - ✗Ждать
4 4, будтоa[:]— ещё одно имя дляa: полный срез создаёт новый список, поэтому вcостаются 3 элемента.
Уточняющие вопросы
- →Что напечатает
print(a is b, a is c)? - →Назовите два других способа скопировать список, кроме
a[:].
JuniorТеорияЧастоЧто произойдёт при запуске программы?
Что произойдёт при запуске программы?
Выведет start, затем ошибка TypeError
Типичные ошибки
- ✗Ждать
{[1, 2]: 'A'}: ключ словаря должен быть хешируемым, аlistизменяемый и не хешируется. Строка вызываетTypeError: unhashable type: 'list'. - ✗Ждать, что ничего не выведется:
TypeErrorвозникает во время выполнения, поэтомуstartуспевает напечататься, а программа останавливается наnames[point] = "A".
Уточняющие вопросы
- →Как изменить
point, чтобы он подошёл в качестве ключа? - →Может ли кортеж, внутри которого список, быть ключом словаря?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
n p 6
Открыть задачу →Типичные ошибки
- ✗Ждать
IndexErrorнаword[-6]: отрицательные индексы идут от-1до-len(word), поэтому в слове из 6 букв-6— это первый символ. - ✗Ждать
5отlen(word), будто это последний индекс:lenсчитает символы — их6, а последний индекс равен5, он же-1.
Уточняющие вопросы
- →Что делает
word[-7]? - →Как напечатать предпоследний символ любой строки?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
[[5, 0], [5, 0]]
Открыть задачу →Типичные ошибки
- ✗Ждать
[[5, 0], [0, 0]], будто* 2создаёт два ряда: внешнее* 2повторяет ссылку на один внутренний список, поэтому оба ряда — один и тот же список. - ✗Ждать, что
5станут все ячейки:[0] * 2повторяет число0, а присваиваниеgrid[0][0]заменяет одну ячейку ряда, а не соседнюю.
Уточняющие вопросы
- →Как построить таблицу 2 × 2, ряды которой — независимые списки?
- →Что напечатает здесь
print(grid[0] is grid[1])?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
True False 1
Открыть задачу →Типичные ошибки
- ✗Считать
(1)кортежем: скобки вокруг одного значения лишь группируют его, поэтомуa— этоint1, иa == 1даётTrue. Кортеж создаёт запятая. - ✗Ждать, что
(1,)равен1:b— кортеж из одного элемента, а кортеж не равенint, поэтомуb == 1даётFalse, аlen(b)равно1.
Уточняющие вопросы
- →Как записать пустой кортеж и какова его длина?
- →Что присваивает
x = 1, 2переменнойx?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
zero 4 True
Открыть задачу →Типичные ошибки
- ✗Ждать, что
orиandдаютTrueилиFalse: они возвращают один из операндов, поэтому0 or "zero"— этоzero, а3 and 4— это4. - ✗Ждать, что
3 and 4даст3:andвозвращает первый ложный операнд, а если оба истинны — последний, поэтому результат4.
Уточняющие вопросы
- →Что выведет
print("" or 0)и почему? - →Почему
notвсегда возвращаетTrueилиFalse, в отличие отor?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
6 5
Открыть задачу →Типичные ошибки
- ✗Ждать
6 6, будтоyследует заx:y = xсвязываетyс объектом5, аx = x + 1переводит на новый объект6толькоx. - ✗Считать
x = x + 1уравнением, которое ничего не меняет:=— это присваивание, Python вычисляетx + 1и связываетxс результатом6.
Уточняющие вопросы
- →Что выведет программа, если третья строка будет
x += 1? - →Что покажет
print(y), если строкаy = xбудет стоять послеx = x + 1?
JuniorТеорияЧастоЧто такое последовательность в Python и какие встроенные типы являются последовательностями?
Что такое последовательность в Python и какие встроенные типы являются последовательностями?
Последовательность — упорядоченный итерируемый объект с доступом по целочисленному индексу через __getitem__ и длиной через __len__. Встроенные: list, tuple, range, str, bytes. Поддерживает индексацию, срезы, in, len() и итерацию.
Типичные ошибки
- ✗Путать «последовательность» с «итерируемым» — множества и словари итерируемы, но не последовательности
- ✗Считать, что последовательность обязана быть изменяемой —
tuple,str,rangeнеизменяемы - ✗Забывать, что
strиbytes— последовательности, а не непрозрачные текстовые блоки
Уточняющие вопросы
- →Как работает срез с отрицательным шагом вроде
s[::-1]? - →Что должен принимать
__getitem__, чтобы объект поддерживал срезы?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
4 2 [1]
Открыть задачу →Типичные ошибки
- ✗Ждать
6, будто|склеивает множества: множество хранит каждое значение один раз, поэтомуa | b— это{1, 2, 3, 4}из4элементов. - ✗Ждать
[1, 4]отa - b: разность оставляет то, что есть вaи нет вb; элементы, которые есть только вb, отбрасываются.
Уточняющие вопросы
- →Какой оператор даёт элементы, которые есть ровно в одном из двух множеств?
- →Зачем программа оборачивает
a - bвsortedперед выводом?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
bcd ace ef
Открыть задачу →Типичные ошибки
- ✗Ждать
IndexErrorнаs[4:100]: срез урезает границы за концом до длины строки и возвращает то, что есть, —ef. - ✗Ждать
bcdeдляs[1:4], будтоstopвходит в срез: срез останавливается передstopи берёт индексы1,2и3.
Уточняющие вопросы
- →Что возвращает
s[::-1]? - →Почему
s[100]вызывает ошибку, аs[4:100]— нет?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
None 1
Открыть задачу →Типичные ошибки
- ✗Ждать
[1, 2, 3]вresult:sort()сортирует список на месте и возвращаетNone, а новый отсортированный список возвращает функцияsorted(). - ✗Ждать, что
numsостанется[3, 1, 2]:sort()меняет сам список, на котором вызван, поэтому после вызоваnums[0]равно1.
Уточняющие вопросы
- →Как получить отсортированную копию и оставить
numsбез изменений? - →Что напечатает
print(nums.append(4))?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
[2, 3] 2 3
Открыть задачу →Типичные ошибки
- ✗Ждать, что
bстанет4, будтоaменяется первым: правая частьb, a + bвычисляется целиком по старым значениям и даёт2, 3, а уже потом меняются оба имени. - ✗Ждать кортеж
(2, 3): имя со звёздочкой всегда собирает значения вlist, что бы ни стояло справа.
Уточняющие вопросы
- →Что попадёт в
middleприfirst, *middle, last = [1, 2]? - →Что произойдёт при
a, b = [1, 2, 3]?
JuniorТеорияЧастоЧто произойдёт при запуске программы?
Что произойдёт при запуске программы?
Выведет hello, затем ошибка TypeError
Типичные ошибки
- ✗Ждать
Jello, будто строку можно изменить на месте: строки неизменяемые, поэтомуs[0] = "J"вызываетTypeError, и второйprintне выполняется. - ✗Ждать ошибку до
hello: до запуска файл останавливает только синтаксическая ошибка, а эта возникает на присваивании, после первогоprint.
Уточняющие вопросы
- →Как получить строку
Jelloизs? - →Какая часть сообщения об ошибке называет тип, который нельзя изменить?
JuniorТеорияЧастоЧто произойдёт при запуске программы?
Что произойдёт при запуске программы?
Ошибка TypeError
Типичные ошибки
- ✗Ждать
Age: 30, будто Python сам превратит30в текст:+не превращает число в текст, поэтомуstrплюсintдаётTypeError; нужноstr(age). - ✗Ждать
ValueError: он возникает при плохом значении вродеint("2.5"), а смешениеstrиintв+— ошибка типа.
Уточняющие вопросы
- →Как изменить вторую строку, чтобы она напечатала
Age: 30? - →Что выведет
print("5" * 3)и почему это не ошибка?
JuniorКодЧастоЧто выведут эти выражения or / and?
Что выведут эти выражения or / and?
(1) default — or возвращает первый истинный операнд (пустой список ложен). (2) 5 — первое истинное значение. (3) '' — and возвращает первый ложный операнд или последний, если все истинны. or/and возвращают один из операндов, а не bool — основа идиомы x = val or default.
Типичные ошибки
- ✗Считать, что
or/andвсегда возвращают булево - ✗Думать, что они всегда возвращают первый операнд независимо от истинности
- ✗Не знать, что идиома
x = val or defaultопирается на это
Уточняющие вопросы
- →Почему сокращённое вычисление возвращает операнд, а не bool?
- →В чём риск
x = val or default, когдаvalможет быть0или''?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
False True False True
Открыть задачу →Типичные ошибки
- ✗Ждать, что
bool("0")дастFalse: строка ложна, только когда она пуста, а в"0"есть один символ, поэтому она истинна. - ✗Ждать, что
bool(" ")дастFalse: пробел — это символ, поэтому" "не пуста, а ложна только пустая строка"".
Уточняющие вопросы
- →Что возвращает
bool(None)? - →Как проверить, что в строке есть что-то кроме пробелов?
JuniorТеорияЧастоКак работает распаковка a, b, c = seq и звёздочка *rest?
Как работает распаковка a, b, c = seq и звёздочка *rest?
Правая часть итерируется и позиционно привязывается к целям; количество должно совпадать, иначе будет ValueError. Одна звёздная цель вроде a, *rest = seq собирает излишек в list, поэтому может стоять где угодно. Работает с любым итерируемым.
Типичные ошибки
- ✗Забывать, что несовпадение количества бросает
ValueError, а не молча обрезает - ✗Думать, что
*restсобирает вtuple, а не вlist - ✗Считать, что звёздная цель обязана быть последней, а не может стоять где угодно
Уточняющие вопросы
- →Что привяжет
a, *b, c = range(5)к каждому имени? - →Как распаковка заставляет обмен
a, b = b, aработать без временной переменной?
JuniorТеорияЧастоЧто выдают zip, enumerate и range при итерации?
Что выдают zip, enumerate и range при итерации?
range(n) лениво выдаёт целые 0..n-1; enumerate(seq) выдаёт пары (индекс, элемент); zip(a, b) выдаёт кортежи, объединяя элементы, пока не кончится самый короткий вход. Все три — ленивые итераторы в Python 3; оберни в list(), чтобы материализовать. zip(*rows) транспонирует.
Типичные ошибки
- ✗Ждать, что они вернут списки, а не ленивые итераторы в Python 3
- ✗Думать, что
zipдобивает до самого длинного входа, а не останавливается на коротком - ✗Считать, что
enumerateначинает с 1 или чтоrange(n)включаетn
Уточняющие вопросы
- →Как заставить
enumerateначинать счёт с 1? - →Что делает
zip(*matrix)и почему он транспонирует?
MiddleТеорияЧастоЧто возвращают dict.items(), keys() и values() в Python 3?
Что возвращают dict.items(), keys() и values() в Python 3?
Они возвращают динамические объекты-представления, а не списки. Представление отражает изменения dict вживую, поддерживает len(), итерацию и in, а ключи и пары ведут себя как множества. В Python 2 возвращались списки; в Python 3 умолчанием стали представления.
Типичные ошибки
- ✗Думать, что
keys()возвращает список — в Python 3 это живое представление - ✗Править
dictво время итерации его представления — поднимаетRuntimeError - ✗Считать представление одноразовым итератором; оно повторно итерируемо и поддерживает
len()
Уточняющие вопросы
- →Почему итерация представления при изменении
dictможет поднятьRuntimeError? - →В каком смысле представления ключей и пар «как множества»?
MiddleТеорияЧастоЧто делает объект хешируемым в Python?
Что делает объект хешируемым в Python?
Объект хешируемый, если у него есть стабильный __hash__ (не меняется в течение жизни) и __eq__, причём равные объекты обязаны хешироваться одинаково. Неизменяемые встроенные (int, str, хешируемый tuple) подходят; изменяемые list, dict, set — нет.
Типичные ошибки
- ✗Думать, что любой объект с
__eq__хешируем — определение__eq__без__hash__делает его нехешируемым - ✗Считать
tupleвсегда хешируемым — это не так, если он содержит изменяемый элемент - ✗Полагать, что изменяемые объекты можно хешировать, лишь бы их не менять
Уточняющие вопросы
- →Что происходит с хешируемостью, если определить
__eq__, но не__hash__? - →Почему равные объекты обязаны иметь равные хеши?
MiddleКодЧастоЧто выведет эта проверка is против == для целых?
Что выведет эта проверка is против == для целых?
True False, затем True True. CPython кэширует малые целые от −5 до 256, поэтому a и b — один объект (is → True); 257 вне этого диапазона, поэтому c и d — разные объекты (is → False). == сравнивает значение и всегда True. Используйте is лишь для тождества (например, x is None).
Типичные ошибки
- ✗Использовать
isдля сравнения значений целых вместо== - ✗Считать, что равные целые всегда один и тот же объект
- ✗Думать, что
==делегирует кis
Уточняющие вопросы
- →Почему CPython кэширует целые от −5 до 256?
- →Когда
is— правильный выбор вместо==?
MiddleТеорияЧастоПочему поиск в dict/set быстрее, чем в list/tuple?
Почему поиск в dict/set быстрее, чем в list/tuple?
dict и set — хеш-таблицы: поиск в среднем O(1) — Python хеширует ключ и переходит к ячейке. У list и tuple нет индекса по значениям, поэтому x in seq сканирует линейно за O(n). Цена: нужны хешируемые элементы и больше памяти.
Типичные ошибки
- ✗Называть поиск в
dictO(log n) — это в среднем O(1) через хеширование, а не обход дерева - ✗Считать проверку
inдля списка дешёвой — это линейный проход O(n) - ✗Забывать, что хеш-таблицы платят за скорость памятью и хешируемостью
Уточняющие вопросы
- →Какова худшая сложность поиска в
dictи когда она наступает? - →Почему нельзя сделать проверку вхождения за O(1) в
list?
MiddleТеорияЧастоКак Python сравнивает две последовательности операторами < и ==?
Как Python сравнивает две последовательности операторами < и ==?
Поэлементно, слева направо (лексикографически). == истинно, только если длины равны и каждая пара равна. Для < Python сравнивает элементы на первом отличии; если одна сторона кончилась раньше, короткая считается меньшей. Элементы должны быть сравнимы, иначе TypeError.
Типичные ошибки
- ✗Думать, что длинные последовательности автоматически больше
- ✗Считать, что
==сравнивает идентичность, а не значения поэлементно - ✗Ждать, что сравнение элементов разных типов приведёт их, а не бросит
TypeError
Уточняющие вопросы
- →Как разрешается
(1, 2) < (1, 2, 3), когда короткая — префикс длинной? - →Почему сравнение
[1, 'a']с[1, 2]бросает ошибку лишь иногда?
MiddleТеорияЧастоЧем str отличается от bytes, и что делают encode/decode?
Чем str отличается от bytes, и что делают encode/decode?
str — неизменяемая последовательность кодовых точек Unicode; bytes — последовательность сырых октетов 0–255. encode превращает str в bytes через кодек вроде UTF-8; decode делает обратное. Их смешивание или неверный кодек бросает UnicodeError; неявного преобразования в Python 3 нет.
Типичные ошибки
- ✗Путать направление
encode(str→bytes) иdecode(bytes→str) - ✗Ждать неявного преобразования str/bytes, как было в Python 2
- ✗Забывать, что неверный кодек бросает
UnicodeDecodeError, а не молча выдаёт кракозябры
Уточняющие вопросы
- →Почему
'café'.encode('ascii')бросает ошибку и как обработчики вроде'ignore'это меняют? - →Сколько байт занимает не-ASCII символ в UTF-8 против UTF-16?
JuniorКодИногдаЧто выведет print(True + 4) и почему?
Что выведет print(True + 4) и почему?
Выводит 5, 2, 2. В Python bool — подкласс int, где True == 1, а False == 0, поэтому булевы значения участвуют в арифметике напрямую. True + 4 — это 1 + 4, а sum булевых считает количество True. Поэтому суммирование списка условий подсчитывает, сколько из них истинны.
Типичные ошибки
- ✗Думать, что
boolне связан сintи не может участвовать в арифметике - ✗Ожидать, что
True + 4возбудитTypeError - ✗Забывать, что
sumбулевых подсчитывает количествоTrue
Уточняющие вопросы
- →Почему
Trueи1могут схлопнуться в один ключdict? - →Что вернёт
isinstance(True, int)и почему?
JuniorКодИногдаЧто выведут эти цепочечные сравнения?
Что выведут эти цепочечные сравнения?
Все три печатают True. Python связывает сравнения неявным and: (1) это 1 < 2 and 2 < 3; (2) это 3 > 2 and 2 == 2; (3) это (False == False) and (False in [False]) → True and True. Третья удивляет, ведь == и in связываются вместе.
Типичные ошибки
- ✗Читать
a < b < cкак(a < b) < cвместо неявногоand - ✗Не понимать, что
==иinсвязываются вместе в одном выражении - ✗Ожидать, что цепочечное сравнение вернёт операнд, а не bool
Уточняющие вопросы
- →Как Python вычисляет средний операнд цепочки ровно один раз?
- →Почему
False == False in [False]— это не(False == False) in [False]?
JuniorТеорияИногдаЧто такое коллизия хеша?
Что такое коллизия хеша?
Коллизия хеша — это два разных ключа, дающих одно хеш-значение или попадающих в одну ячейку таблицы. dict и set разрешают её пробированием других ячеек, поэтому коллизия стоит скорости поиска, а не корректности — неверное значение не вернётся.
Типичные ошибки
- ✗Думать, что коллизии заставляют
dictвозвращать неверные значения — они лишь добавляют накладные расходы на пробирование - ✗Считать, что встроенные типы никогда не дают коллизий
- ✗Путать равные хеши с равными ключами — равный хеш не значит равный ключ
Уточняющие вопросы
- →Как CPython
dictвыбирает следующую ячейку после коллизии? - →Что происходит с производительностью
dictпо мере роста заполненности?
JuniorКодИногдаПосле b = a на словаре что выведут оба после правок?
После b = a на словаре что выведут оба после правок?
Оба выводят {1: 1, 2: 2, 3: 3}. b = a связывает второе имя с тем же объектом-словарём — не копирует его, поэтому правки через любое имя видны через оба. Для независимого словаря используйте a.copy() или dict(a) (поверхностная копия). a is b здесь True.
Типичные ошибки
- ✗Думать, что
b = aкопирует словарь, а не создаёт псевдоним - ✗Ожидать, что правки через одно имя не затронут другое
- ✗Путать связывание имени с дублированием объекта
Уточняющие вопросы
- →Как сделать
bнезависимой поверхностной копиейa? - →Что вернёт
a is bздесь и что это проверяет?
JuniorКодИногдаЧто даст [1, 2, 3].extend('abc') и почему?
Что даст [1, 2, 3].extend('abc') и почему?
Выводит [1, 2, 3, 'a', 'b', 'c']. extend перебирает свой аргумент и добавляет каждый элемент; строка итерируется по символам, поэтому каждый символ добавляется отдельно. Чтобы добавить всю строку одним элементом, используйте l.append('abc'), дающий [1, 2, 3, 'abc'].
Типичные ошибки
- ✗Ожидать, что
extendдобавит строку одним элементом, какappend - ✗Думать, что
extendотвергает всё, что не является списком - ✗Забывать, что строка итерируется по символам
Уточняющие вопросы
- →Как добавить всю строку одним элементом?
- →Что возбудит
[1, 2].extend(3)и почему?
JuniorКодИногдаПреобразовать список одноэлементных кортежей в список строк
Преобразовать список одноэлементных кортежей в список строк
Берём первый элемент каждого кортежа: [t[0] for t in data]. Одноэлементный кортеж записывается ('x',) с запятой в конце, поэтому индексация [0] достаёт единственное значение. Эквивалентные формы — [v for (v,) in data] (распаковка кортежа в цели цикла) или list(map(lambda t: t[0], data)).
Типичные ошибки
- ✗Забывать, что одноэлементный кортеж требует
[0]для разворачивания - ✗Думать, что
str(t)чисто даёт внутреннее значение - ✗Путать индекс внешнего списка с индексом внутреннего кортежа
Уточняющие вопросы
- →Как развернуть кортежи, которые могут держать больше одного значения?
- →Почему
('x')отличается от('x',)?
JuniorКодИногдаЧто выведет 0.1 + 0.2 == 0.3?
Что выведет 0.1 + 0.2 == 0.3?
False. 0.1 + 0.2 равно 0.30000000000000004, потому что эти значения не представимы точно в двоичной плавающей точке, поэтому сумма отличается от литерала 0.3. Сравнивайте с допуском — math.isclose(0.1 + 0.2, 0.3).
Типичные ошибки
- ✗Считать, что литералы float хранятся точно
- ✗Сравнивать float через
==вместо допуска - ✗Думать, что Python по умолчанию использует десятичную арифметику для
float
Уточняющие вопросы
- →Как
math.iscloseрешает, достаточно ли близки два float? - →Когда стоит взять
decimal.Decimalилиfractions.Fraction?
JuniorКодИногдаКак напечатать литеральные {} внутри f-строки?
Как напечатать литеральные {} внутри f-строки?
Удвойте скобки: print(f'Curly brackets: {{}}') печатает Curly brackets: {}. В f-строке {{ — экранированная литеральная {, а }} — литеральная }; одиночные {...} — поле подстановки, требующее выражения, поэтому пустые {} возбуждают SyntaxError на этапе компиляции.
Типичные ошибки
- ✗Пытаться экранировать скобки обратным слешем вместо удвоения
- ✗Думать, что пустые
{}допустимы и молча пропускаются - ✗Путать экранирование
{{}}в f-строке с особенностямиstr.format
Уточняющие вопросы
- →Как вставить литеральные
{x}рядом с подставляемымx? - →Когда возбуждается
SyntaxErrorдля пустых{}— при компиляции или выполнении?
JuniorКодИногдаЧто выведет отрицательная индексация 'abyz'[-1]?
Что выведет отрицательная индексация 'abyz'[-1]?
Выводит z, затем y. Отрицательный индекс отсчитывается с конца: -1 — последний элемент, -2 — предпоследний. Это эквивалентно var[len(var) - 1]. Работает на любой последовательности — str, list, tuple — а выход за диапазон возбуждает IndexError.
Типичные ошибки
- ✗Думать, что
-1указывает на первый элемент, а не последний - ✗Считать, что отрицательные индексы по умолчанию возбуждают
IndexError - ✗Забывать, что отрицательная индексация работает на любой последовательности, не только строках
Уточняющие вопросы
- →Что вернёт срез
var[-2:]для этой строки? - →Когда отрицательный индекс действительно возбуждает
IndexError?
MiddleКодИногдаЧто выведут += и + для этих связанных списков?
Что выведут += и + для этих связанных списков?
[1, 2, 3, 4], затем [1, 2, 3]. += для списка вызывает __iadd__, изменяя объект на месте, поэтому b (тот же объект) это видит. a = a + [4] строит новый список и переназначает a, оставляя b на исходном — изменение против переназначения.
Типичные ошибки
- ✗Считать, что
+=и+ведут себя одинаково для списков - ✗Думать, что
b = aкопирует список - ✗Не различать изменение на месте и переназначение
Уточняющие вопросы
- →Что возвращает
__iadd__и почему это важно для переназначения? - →Как изменился бы результат, будь
aкортежем, а не списком?
MiddleКодИногдаЧто выведет [[0]*3]*3 после записи?
Что выведет [[0]*3]*3 после записи?
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]. Внешнее * 3 копирует ссылку на один и тот же внутренний список трижды, поэтому изменение одной строки меняет все. Решение — включение, строящее независимые строки: grid = [[0] * 3 for _ in range(3)].
Типичные ошибки
- ✗Считать, что
* 3делает глубокую копию внутреннего списка - ✗Путать умножение списка с трансляцией NumPy
- ✗Не знать решение через включение для независимых строк
Уточняющие вопросы
- →Почему
[[0] * 3 for _ in range(3)]строит независимые строки? - →Применима ли та же ловушка алиасинга к
[0] * 3из неизменяемых int?
MiddleКодИногдаЧто произойдёт при t[0] += [100] на кортеже?
Что произойдёт при t[0] += [100] на кортеже?
(1) проходит → ([1, 99], 2): кортеж неизменяем, но список, на который он ссылается, изменяем. (2) — знаменитая ловушка: += изменяет список И пытается переназначить t[0], поэтому возникает TypeError — но список всё равно изменяется до [1, 99, 100]. Операция выполняется наполовину.
Типичные ошибки
- ✗Думать, что неизменяемость кортежа защищает изменяемые объекты, которые он держит
- ✗Ожидать, что строка (2) оставит список неизменным после ошибки
- ✗Считать, что
TypeErrorоткатывает изменение на месте
Уточняющие вопросы
- →Почему
+=и изменяет список, и пытается присвоить вt[0]? - →Что это говорит о том, действительно ли кортеж «неизменяем»?
MiddleДебаггингРедкоПочему это среднее возвращает неверное значение?
Почему это среднее возвращает неверное значение?
// — это деление с округлением вниз, поэтому average([1, 2]) возвращает 1, а не 1.5. Пустой список также вызывает ZeroDivisionError. Решение: использовать / для истинного деления и защитить пустой случай: return sum(nums) / len(nums) if nums else 0.
Типичные ошибки
- ✗Путать
//(округление вниз) с/(истинное деление) - ✗Думать, что
//округляет к ближайшему, а не к минус бесконечности - ✗Забывать про ZeroDivisionError на пустом списке
Уточняющие вопросы
- →Что делает
//с отрицательными операндами, например-7 // 2? - →Как вычислить целочисленное среднее, всё же округляя корректно?
MiddleДебаггингРедкоПочему этот счётчик слов возбуждает KeyError?
Почему этот счётчик слов возбуждает KeyError?
counts[w] += 1 читает counts[w] до того, как ключ существует → KeyError при первой встрече каждого слова. Решение: counts[w] = counts.get(w, 0) + 1, либо collections.defaultdict(int), либо просто collections.Counter(text.split()).
Типичные ошибки
- ✗Считать, что
dict[k] += 1авто-инициализирует отсутствующий ключ нулём - ✗Винить
splitили возврат, а не чтение отсутствующего ключа - ✗Не использовать
defaultdictилиCounter
Уточняющие вопросы
- →Как
collections.defaultdict(int)меняет поведение при отсутствующем ключе? - →Когда
dict.setdefaultпредпочтительнееgetдля этого паттерна?
MiddleДебаггингРедкоПочему удаление элементов при итерации пропускает их?
Почему удаление элементов при итерации пропускает их?
Удаление элементов при итерации сдвигает индексы под итератором, поэтому он пропускает элементы. Никогда не изменяйте список, по которому идёте. Решение: итерировать копию (for n in nums[:]:) или пересобрать включением: nums = [n for n in nums if n % 2].
Типичные ошибки
- ✗Считать, что удаление оставляет итератор на следующем элементе
- ✗Ожидать RuntimeError на
remove(он про изменение размера, но индексы всё равно сдвигаются) - ✗Изменять список на месте вместо итерации копии или пересборки
Уточняющие вопросы
- →Почему итерация по
nums[:]делает удаление безопасным? - →Как включение вообще избегает изменения списка?
MiddleДебаггингРедкоПочему этот цикл сборки строки медленный на больших данных?
Почему этот цикл сборки строки медленный на больших данных?
Строки неизменяемы, поэтому каждое s += ... строит совершенно новую строку — O(n²) для n слов, медленно на больших данных. Функционально верно, но сложность и есть ошибка. Решение: return ' '.join(words), что даёт O(n) и идиоматично.
Типичные ошибки
- ✗Считать, что
+=на строке изменяет буфер на месте - ✗Винить хвостовой пробел или
stripвместо повторных выделений - ✗Микрооптимизировать конкатенацию вместо использования
join
Уточняющие вопросы
- →Почему неизменяемость строк вынуждает новое выделение на каждое
+=? - →Когда
io.StringIOили список сjoinпредпочтительнее для сборки текста?
SeniorТеорияРедкоКак CPython dict хранит элементы и выполняет ресайз?
Как CPython dict хранит элементы и выполняет ресайз?
CPython dict — разреженная хеш-таблица; ячейка хранит хеш ключа плюс ссылки на ключ и значение. Поиск берёт младшие биты хеша как смещение ячейки и пробирует при коллизии. Около трети ячеек держится пустыми; при заполнении таблица растёт, и записи вставляются заново.
Типичные ошибки
- ✗Думать, что CPython
dictиспользует раздельное связывание — он использует открытую адресацию с пробированием - ✗Считать, что таблица никогда не ресайзится — она растёт, как только заполнится
- ✗Полагать, что слот индексируется полным хешем — сначала только младшими битами
Уточняющие вопросы
- →Зачем держать треть ячеек пустыми, а не заполнять таблицу плотно?
- →Как «компактный dict» в 3.6 изменил раскладку и добавил порядок вставки?
SeniorТеорияРедкоВо что вычисляется {True: 'a', 1: 'b', 1.0: 'c'} и почему?
Во что вычисляется {True: 'a', 1: 'b', 1.0: 'c'} и почему?
Вычисляется в {True: 'c'}. True, 1, 1.0 равны (True == 1 == 1.0) и хешируются одинаково, поэтому это один ключ. Первый вставленный ключ (True) сохраняется, но каждое присваивание перезаписывает значение — итог 'c'.
Типичные ошибки
- ✗Думать, что разные типы означают разные ключи — равенство плюс равный хеш схлопывают их
- ✗Считать, что выживший ключ — последний записанный — это первый вставленный
- ✗Забывать, что значение всё же обновляется при каждом повторном присваивании равному ключу
Уточняющие вопросы
- →Какую идентичность имеет выживший ключ —
Trueили1? - →Как это схлопывание может вызвать реальный баг в таблице поиска?