Тест самоперевірки

Списки

Обери відповідь — і одразу побачиш пояснення. Помилитись тут корисніше, ніж на іспиті.

← до лекції
Відповіді0 / 8   правильних: 0
Питання 1 / 8

покупки = ["хліб", "молоко", "яблука"], далі покупки.append(["сир", "мед"]). Чому дорівнює len(покупки)?

Чому так. append завжди додає рівно один елемент — тут цим елементом став цілий список, тому в комірці 3 лежить посилання на вкладений список і довжина дорівнює 4. Спокуслива пʼятірка — це відповідь для extend: він розкриває послідовність і додає кожен її елемент окремо.
Питання 2 / 8

Що надрукує print(покупки) після рядка покупки = покупки.sort()?

Чому так. sort() сортує список на місці й повертає None. Це None ми й поклали в імʼя «покупки», втративши сам список. Методи, що змінюють список на місці, викликають окремим рядком, без присвоєння; коли потрібен саме результат — беруть функцію sorted().
Питання 3 / 8

оригінал = [["хліб"], ["мед"]]; копія = оригінал[:]; копія[0].append("сир"). Що тепер в оригіналі?

Чому так. Зріз створює поверхневу копію: зовнішні комірки нові, але в них лежать ті самі посилання, що й в оригіналі. Тому копія[0] і оригінал[0] — це один і той самий вкладений список, і зміна видна з обох боків. Захистив би лише copy.deepcopy. Якби ми додавали в саму копію (копія.append), верхній рівень зріз би вберіг.
Питання 4 / 8

sorted(["сир", "хліб", "яблука", "молоко", "мед"], key=len) починається з 'сир', 'мед'. Чому саме в такому порядку, а не 'мед', 'сир'?

Чому так. Ключ у обох слів однаковий (3), а сортування в Python стабільне — це гарантія мови, а не збіг реалізації: елементи з рівним ключем ніколи не міняються місцями. «Сир» стояв у вихідному списку раніше за «мед», тому й лишився попереду. За абеткою було б навпаки — і саме це видно в інтерактиві 4.
Питання 5 / 8

У списку 10 000 елементів. Скільки комірок доведеться фізично зсунути виклику покупки.insert(0, "мед")?

Чому так. Список — суцільний масив вказівників, місця перед нульовим елементом просто немає, тому всі 10 000 їдуть на комірку вправо: це O(n). append не зсуває нічого — у нього O(1) амортизовано. Коли додавати треба з обох кінців, беруть collections.deque, де appendleft коштує O(1).
Питання 6 / 8

Чому append вважають O(1) «амортизовано», а не просто O(1)?

Чому так. Ємність росте блоками за формулою (n + n//8 + 6), заокругленою вниз до кратного 4: 4, 8, 16, 24, 32, 40, 52, 64… Між стрибками append просто кладе вказівник у готову комірку. Оскільки запас пропорційний розміру, перевиділення стають дедалі рідшими, і середня ціна одного append — стала (в інтерактиві 7 це видно як «копій на append»).
Питання 7 / 8

поле = [[0] * 3] * 3, далі поле[0][0] = 9. Що надрукує print(поле)?

Чому так. Внутрішній вираз [0] * 3 обчислився один раз і створив один список. Зовнішнє множення на 3 не копіювало його — воно поклало в три комірки три посилання на цей самий обʼєкт. Тому «три рядки» насправді один рядок, показаний тричі. Правильний рецепт — спискове включення: [[0] * 3 for _ in range(3)], де вираз обчислюється заново на кожен рядок.
Питання 8 / 8

Список покупок містить ["хліб", "мед", "хліб"]. Що зробить покупки.remove("хліб")?

Чому так. remove шукає за значенням і прибирає тільки перше входження, повертаючи None (елемент повертає pop, а не remove). Небезпека в іншому: якщо шуканого значення в списку немає взагалі, remove кине ValueError і зупинить програму — тому перед видаленням варто спитати «if "хліб" in покупки».