У файлі prays.txt три рядки. Програма виконує open("prays.txt", "w", encoding="utf-8"), а вже наступний рядок падає з винятком — жодного write не було. Що тепер у файлі?
Чому так. Режим «w» обрізає файл до нуля байтів на етапі open, а не на етапі write — це робить операційна система за прапорцем O_TRUNC. Інтерактив 1 показує це середньою колонкою: 3 → 0 ще до запису. Спокуслива перша відповідь виходить з того, що «нічого не записали — нічого не змінилось», але зміна вже сталася. Якщо намір саме «створити новий файл», безпечніший режим «x»: він кине FileExistsError і не втратить жодного байта.
Питання 2 / 8
Усередині блоку with open(...) as f стався ZeroDivisionError. Що буде далі?
Чому так. Менеджер контексту гарантує прибирання, а не тишу: на виході з блоку — будь-якому, включно з винятком — викликається close(), після чого виняток продовжує підніматись по стеку. Це той самий try/finally з теми 18, а не try/except. Найчастіша хибна очікуваність новачка — саме перший варіант; щоб виняток справді обробити, потрібен окремий try/except.
Питання 3 / 8
Файл записали з encoding="utf-8", а прочитали з encoding="cp1251". Що станеться?
Чому так. У cp1251 будь-який байт від 0 до 255 щось означає, тому декодування не може провалитись — 13 байтів UTF-8 просто стануть 13 однобайтовими символами: «хліб;28.5» перетвориться на «С…Р»С–Р±;28.5». Це тиха біда: програма працює далі, а мотлох іде в базу. UnicodeDecodeError буває у зворотному напрямку (записали cp1251, читаємо utf-8), і саме тому гучна біда краща за тиху.
Питання 4 / 8
Треба порахувати, у скількох рядках чотиригігабайтного журналу трапляється слово ERROR. Який спосіб читання обрати?
Чому так. read() і readlines() тягнуть увесь файл у памʼять і ростуть разом із ним; readlines() ще й дорожчий, бо платить окремим обʼєктом за кожен рядок. Обхід циклом тримає близько восьми кілобайтів незалежно від розміру файлу — саме це показує інтерактив 4 у крайньому правому положенні повзунка. Різниця не в швидкості читання з диска, а в піковій памʼяті.
Питання 5 / 8
Чому дорівнює Path("dani/arhiv.tar.gz").stem?
Чому так. .stem зрізає рівно одне — останнє — розширення, тому лишається «arhiv.tar». Щоб отримати всі розширення, є .suffixes, який поверне [ʼ.tarʼ, ʼ.gzʼ]. Спокуслива відповідь «arhiv» — це результат імʼя.split(ʼ.ʼ)[0], і саме через неї люди одного дня втрачають половину назви файлу на чомусь на кшталт prays.2026.csv.
Питання 6 / 8
У теці projekt лежать main.py, README.md і підтека utils із chytach.py. Скільки шляхів поверне Path("projekt").glob("*.py")?
Чому так. Одна зірочка збігається з будь-якими символами, крім роздільника шляху, тому вглиб теки вона не заходить: знайдеться лише main.py. Щоб дістати й вкладені файли, потрібне «**», яке означає «ця тека і всі вкладені» — тоді буде 2. Варіант із текою хибний: utils не закінчується на .py, хоч сам по собі glob теки від файлів і не відрізняє.
Питання 7 / 8
Програма відкрила файл, записала 100 рядків по 14 байтів і не викликала ані flush(), ані close(). Скільки байтів зараз на диску?
Чому так. 1400 байтів менші за буфер у 8192, тому до операційної системи не пішло жодного звернення — файл на диску порожній. Так працює буферизація: системний виклик коштує дорого, і Python накопичує дані, доки буфер не заповниться. Саме тому вихід із with (він викликає close, а той скидає буфер) — не косметика, а умова того, що дані взагалі опиняться на диску.
Питання 8 / 8
Скрипт щоразу створює теку звітів рядком (тека).mkdir(parents=True). Перший запуск пройшов добре. Що буде на другому?
Чому так. parents=True відповідає лише за створення проміжних тек, а не за терпимість до наявної. Щоб повторний запуск не падав, потрібен другий прапорець: exist_ok=True. Саме тому ці два аргументи майже завжди пишуть разом. Варіант «перестворюється порожньою» хибний: mkdir нічого не видаляє — на відміну від open у режимі «w».