Стандартная библиотека и строки
Строки и стандартная библиотека - то, что ты пишешь каждый день, и по ним видно стаж: s += в цикле, decode без кодировки, naive-datetime в мультизонной системе - у каждого «шрама» тут своя история продового инцидента.
// Знание Counter и defaultdict вместо ручных циклов - маленький маркер, который интервьюеры замечают.
Строка иммутабельна, и это дорого в циклах
str неизменяем: каждая конкатенация создаёт новую строку с полным копированием. s += x в цикле на тысячах итераций - O(n²); правильный паттерн - собрать куски в список и склеить одним ''.join(parts).
f-строки - стандарт форматирования: f'{x:.2f}' - точность, f'{x!r}' - repr, f'{s:>10}' - выравнивание, f'{x=}' - отладочный вывод имени и значения.
// split, strip, startswith, replace закрывают 90% разбора строк. Регулярки - когда есть настоящий паттерн, и всегда raw-строками r'...'.
- ''.join(parts)
- линейная склейка списка строк - вместо квадратичного s += в цикле
str против bytes: граница текста и байтов
str - текст (юникод), bytes - байты. Граница пересекается только явно: encode/decode с указанием кодировки, по умолчанию utf-8. Смешение - TypeError в лучшем случае, кракозябры в худшем.
decode без явной кодировки на не-UTF-8 данных - UnicodeDecodeError, который стреляет в проде на первом же «интересном» файле, а не в тестах на ASCII.
// Правило: байты у границ системы (сеть, файлы, base64), текст - внутри; конвертация на границе, один раз.
- encode / decode
- str → bytes / bytes → str; кодировку указывать явно
Инструменты, которые ждут в живом коде
collections: Counter - частоты и most_common без ручного цикла; defaultdict - дефолт по фабрике вместо if key not in; dataclass/namedtuple - именованные записи вместо «магических» индексов кортежа.
pathlib.Path - современные пути: оператор / для склейки, glob, read_text; os.path - легаси-стиль. json: ensure_ascii=False для кириллицы, default= для datetime и Decimal.
// datetime: naive и aware (с таймзоной) не сравниваются и не вычитаются между собой. Дисциплина - UTC внутри системы, локализация только на выводе.
- naive / aware
- datetime без таймзоны / с ней; смешивать нельзя
Как отвечать: «Как эффективно склеить тысячи строк?»
Не s += в цикле: строка иммутабельна, каждая склейка копирует всё накопленное это O(n²). Собираю куски в список и склеиваю одним ''.join(parts) - линейно и идиоматично; join принимает любое итерируемое, так что подойдёт и генератор без промежуточного списка. Для инкрементальной сборки в потоке есть io.StringIO. И бонус: для путей это же правило - pathlib и /, а не строковая склейка со слешами.
Причина (иммутабельность) названа до решения, плюс два уместных инструмента - ответ не заучен, а понят.
На чём валят
- −s += в цикле на тысячах итераций - квадратичная склейка.
- −Сравнение строк без нормализации регистра и пробелов - «одинаковые» не равны.
- −decode без кодировки на не-UTF-8 данных - UnicodeDecodeError в проде, не в тесте.
- −datetime.now() без tz в системе с несколькими зонами - сдвиги на часы.
- −Парсить JSON регулярками - есть json.loads.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 14, остальные разбираются в тренажёре.
- Зачем использовать enumerate и zip вместо range(len(...)) с индексацией?A)Они переводят коллекцию в строку и обратно, что зачем-то необходимо для корректного прохода по её элементам в цикле forB)Они предварительно сортируют коллекцию, поэтому цикл по enumerate идёт в возрастающем порядке элементовC)enumerate даёт пары (индекс, элемент), zip идёт по нескольким коллекциям параллельно — читаемее и без ошибок с индексамиD)Они кешируют длину коллекции, за счёт чего цикл по ним оказывается в разы быстрее другого способа её обхода в Python
показать ответ и разбор
+C)enumerate даёт пары (индекс, элемент), zip идёт по нескольким коллекциям параллельно — читаемее и без ошибок с индексами// разбор: enumerate(xs) отдаёт пары (i, x), избавляя от range(len(xs)) и ручного xs[i]; zip(a, b) идёт по нескольким последовательностям сразу, останавливаясь на самой короткой. Это читаемее, короче и не порождает классических ошибок с выходом за границы и рассинхроном индексов. Сортировки и особого ускорения тут нет.
- Чем f-строки обычно предпочтительнее конкатенации через + и %-форматирования?A)F-строки вычисляются на этапе компиляции и поэтому запрещают вставлять внутрь себя изменяемые переменныеB)Ничем: f-строки — устаревший синтаксис, а современный рекомендованный способ форматирования — это оператор % с круглыми скобкамиC)F-строки работают лишь с числами и не дают подставить внутрь фигурных скобок строку или результат вызова методаD)Читаемее и обычно быстрее: выражение прямо в {…}, сразу видно, что куда подставляется; + требует ручных приведений, % легко сбить
показать ответ и разбор
+D)Читаемее и обычно быстрее: выражение прямо в {…}, сразу видно, что куда подставляется; + требует ручных приведений, % легко сбить// разбор: f-строка f'{name}: {price*qty:.2f}' подставляет выражения прямо по месту — читается сверху вниз, поддерживает форматные спецификаторы и обычно быстрее .format и %. Конкатенация через + требует ручного приведения типов и легко путается, а %-стиль легко рассинхронизировать по позициям аргументов. Отсюда f-строки как способ по умолчанию.
- Нужно посчитать, сколько раз каждый элемент встречается в списке. Идиоматичный инструмент?A)Отсортировать список и вручную пройтись циклом, наращивая счётчик и сбрасывая его при каждой смене соседнего значения в проходеB)collections.Counter(список): считает частоты за один проход, а его метод most_common(k) сразу отдаёт k самых частых элементовC)Создать обычный список нулей длиной со словарь и вручную инкрементировать индексы, соответствующие каждому встреченному элементуD)Множество set(список): оно само хранит, сколько раз каждый элемент был в него добавлен, и отдаёт это по запросу количества
показать ответ и разбор
+B)collections.Counter(список): считает частоты за один проход, а его метод most_common(k) сразу отдаёт k самых частых элементов// разбор: Counter из collections принимает итерируемое и возвращает подсчёт {элемент: частота} за один проход, а его метод most_common(k) сразу выдаёт k самых частых. Ручные варианты через сортировку или ручной словарь-счётчик многословнее и легче ошибиться, а set хранит только уникальные элементы, без счётчиков.
- Чем pathlib.Path лучше склейки путей строками через '/' или '+'?A)Ничем: ручная конкатенация путей строкой через плюс — рекомендованный и самый переносимый способ работы с файловой системойB)Pathlib работает в основном в Linux, поэтому в кроссплатформенном коде его применение не рекомендуетсяC)Кроссплатформенный разделитель, склейка оператором / и методы (.exists, .suffix, .parent) вместо хрупкой ручной возни со строкамиD)Pathlib при обращении к объекту Path автоматически создаёт на диске все несуществующие директории из этого пути без спроса
показать ответ и разбор
+C)Кроссплатформенный разделитель, склейка оператором / и методы (.exists, .suffix, .parent) вместо хрупкой ручной возни со строками// разбор: Path сам подставляет правильный разделитель под ОС, склеивается оператором / — Path('dir') / 'file.txt' — и даёт удобные методы и свойства: .exists(), .parent, .suffix, .name, .read_text(). Это надёжнее хрупких строковых манипуляций и os.path.join. Директории Path сам не создаёт — для этого есть явный .mkdir().
- В чём разница между str и bytes в Python 3 и зачем нужны encode/decode?A)Str и bytes — это полные синонимы в Python 3, а методы encode/decode оставлены в языке лишь для совместимости со старым кодом на Python 2B)str — последовательность символов Unicode, bytes — сырые байты; encode (str→bytes) и decode (bytes→str) переводят между ними по кодировкеC)Bytes хранит внутри себя ASCII-символы, а str — цифры; для иных данных оба типа непригодныD)Разница между ними в скорости работы: bytes ощутимо быстрее str, а по смыслу это один и тот же тип данных
показать ответ и разбор
+B)str — последовательность символов Unicode, bytes — сырые байты; encode (str→bytes) и decode (bytes→str) переводят между ними по кодировке// разбор: В Python 3 str — это текст (последовательность Unicode-символов), а bytes — сырые байты (например, из файла или сети). Между ними переводят явно: str.encode(кодировка) даёт bytes (в UTF-8 «é» → 2 байта, проверено), bytes.decode(кодировка) — обратно в str. Смешивать их нельзя, а неверная кодировка при decode ломается или портит текст. Это не синонимы и не «только про скорость»; путаница str/bytes — источник UnicodeDecodeError.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.