сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Python: ядро языка

Стандартная библиотека и строки

Зачем это спрашивают

Строки и стандартная библиотека - то, что ты пишешь каждый день, и по ним видно стаж: 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, остальные разбираются в тренажёре.

  1. #stdlib_strings1 / 5
    Зачем использовать enumerate и zip вместо range(len(...)) с индексацией?
    A)Они переводят коллекцию в строку и обратно, что зачем-то необходимо для корректного прохода по её элементам в цикле for
    B)Они предварительно сортируют коллекцию, поэтому цикл по enumerate идёт в возрастающем порядке элементов
    C)enumerate даёт пары (индекс, элемент), zip идёт по нескольким коллекциям параллельно — читаемее и без ошибок с индексами
    D)Они кешируют длину коллекции, за счёт чего цикл по ним оказывается в разы быстрее другого способа её обхода в Python
    показать ответ и разбор
    +C)enumerate даёт пары (индекс, элемент), zip идёт по нескольким коллекциям параллельно — читаемее и без ошибок с индексами

    // разбор: enumerate(xs) отдаёт пары (i, x), избавляя от range(len(xs)) и ручного xs[i]; zip(a, b) идёт по нескольким последовательностям сразу, останавливаясь на самой короткой. Это читаемее, короче и не порождает классических ошибок с выходом за границы и рассинхроном индексов. Сортировки и особого ускорения тут нет.

  2. #stdlib_strings2 / 5
    Чем f-строки обычно предпочтительнее конкатенации через + и %-форматирования?
    A)F-строки вычисляются на этапе компиляции и поэтому запрещают вставлять внутрь себя изменяемые переменные
    B)Ничем: f-строки — устаревший синтаксис, а современный рекомендованный способ форматирования — это оператор % с круглыми скобками
    C)F-строки работают лишь с числами и не дают подставить внутрь фигурных скобок строку или результат вызова метода
    D)Читаемее и обычно быстрее: выражение прямо в {…}, сразу видно, что куда подставляется; + требует ручных приведений, % легко сбить
    показать ответ и разбор
    +D)Читаемее и обычно быстрее: выражение прямо в {…}, сразу видно, что куда подставляется; + требует ручных приведений, % легко сбить

    // разбор: f-строка f'{name}: {price*qty:.2f}' подставляет выражения прямо по месту — читается сверху вниз, поддерживает форматные спецификаторы и обычно быстрее .format и %. Конкатенация через + требует ручного приведения типов и легко путается, а %-стиль легко рассинхронизировать по позициям аргументов. Отсюда f-строки как способ по умолчанию.

  3. #stdlib_strings3 / 5
    Нужно посчитать, сколько раз каждый элемент встречается в списке. Идиоматичный инструмент?
    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 хранит только уникальные элементы, без счётчиков.

  4. #stdlib_strings4 / 5
    Чем 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().

  5. #stdlib_strings5 / 5
    В чём разница между str и bytes в Python 3 и зачем нужны encode/decode?
    A)Str и bytes — это полные синонимы в Python 3, а методы encode/decode оставлены в языке лишь для совместимости со старым кодом на Python 2
    B)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.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.