December 9, 2024

Python: Продвинутый уровень. Теоретическое задание.

В этой статье мы разберем тестовые задания продвинутого уровня по Python, состоящего из:

  1. Теоретической части – основные вопросы, которые могут встретиться, и как правильно на них отвечать.
  2. Практической части – решение практического задания с детальным разбором, рассмотрено в статье Python: Продвинутый уровень. Практическое задание

Вопрос №1

Какой из встроенных типов данных Python лучше всего подходит для хранения списка IP-адресов?

Варианты ответа:

  1. Множества
  2. Словари
  3. Кортежи
  4. Списки
  5. Строки

Обоснование:
Для хранения списка IP-адресов оптимальным выбором будет список (list) или множество (set), в зависимости от требований задачи:

  • Список: Подходит, если возможны дублирующиеся IP-адреса, либо если требуется сохранить порядок элементов.
  • Множество: Подходит, если важна уникальность IP-адресов, поскольку множества автоматически устраняют дубли.
  • Словарь: Используется для хранения пар ключ-значение и не подходит для простого хранения списка.
  • Кортеж: Подходит, если данные неизменяемы, но не обладает преимуществами списков или множеств в данном контексте.
  • Строки: Не подходят для хранения коллекций данных, так как это тип для текста.

📌Правильный ответ:

  1. Множества – если важна уникальность IP-адресов.

Вопрос №2

Укажите, каким будет вывод этой программы в случае, если на вход подаются две строки — сначала «Python», потом «exit».

Варианты ответа:

  1. Программа уйдёт в бесконечный цикл
  2. I’mPython
  3. I’m
  4. I’m Python exit
  5. I’m Python

Обоснование:

1. Первая итерация цикла:

  • Вводится "Python". Условие if c == 'exit': не выполняется.
  • Переменная name обновляется: name = '' + 'Python' → name = 'Python'.

2. Вторая итерация цикла:

  • Вводится "exit". Условие if c == 'exit': выполняется.
  • Происходит break, и цикл завершает выполнение.

3. После завершения цикла выполняется вывод: print('I’m', name), где name содержит "Python".

📌Правильный ответ:
2. I’mPython

Пояснение:
Ответ слипается без пробела между "I’m" и "Python", так как в функции print() не добавлен пробел между строками.

Вопрос №3
Какой результат выведет этот код

Обоснование:
В данном коде используется изменяемый тип данных (список) в качестве значения по умолчанию для аргумента a. Это приводит к следующему поведению:

  1. При первом вызове функции add(), список a создается, в него добавляется 'A', и возвращается список ['A'].
  2. При втором вызове функции add(), используется тот же список, что и в первом вызове, поскольку значение по умолчанию для аргумента a сохраняется в памяти. В этот список снова добавляется 'A', и он становится ['A', 'A'].

Такое поведение связано с тем, что значения по умолчанию для аргументов функции в Python вычисляются один раз при определении функции и хранятся в памяти.

📌Правильный ответ:
4. ['A'] и ['A', 'A']

Вопрос №4

Вы работаете с большим набором данных и вам необходимо определить, какая из перечисленных встроенных функций Python наиболее эффективна для определения длины списка с использованием меньшего объема оперативной памяти.

Варианты ответа:

  1. len()
  2. measure()
  3. length()
  4. count()
  5. size()

Обоснование:

  • len(): Это встроенная функция Python, оптимизированная для определения длины коллекций (списки, строки и т.д.). Она выполняется быстро и не требует дополнительной памяти, поскольку доступ к длине коллекции осуществляется напрямую (у списка длина хранится как атрибут объекта).
  • measure(), length(), size(): Такие функции не существуют в стандартной библиотеке Python, вызов их приведёт к ошибке NameError.
  • count(): Эта функция используется для подсчёта количества вхождений определённого элемента в коллекции. Она не подходит для определения длины списка.

📌Правильный ответ:

  1. len()

Пояснение:
Функция len() наиболее эффективна для определения длины списка, так как она оптимизирована для этой задачи и использует минимальный объём оперативной памяти.

Вопрос №5
Какой результат выведет этот код?

Варианты ответа:

  1. Equals и Equals
  2. TypeError: A.meth() missing 1 required positional argument
  3. Not Equals и Equals
  4. Equals и Not Equals
  5. Not Equals и Not Equals

Обоснование:

Первая строка вывода (a.counter == A.counter)

  • a.counter — это экземплярное свойство, которому присвоено значение 100.
  • A.counter — это атрибут класса, который остался равным 0.
  • Сравнение a.counter == A.counter возвращает False, так как 100 != 0.
  • Выводится 'Not Equals'.

Вторая строка вывода (a.meth() == A.meth(a))

  • a.meth() вызывает метод экземпляра, который возвращает строку 'method'.
  • A.meth(a) вызывает тот же метод, передавая явно экземпляр a в качестве аргумента self. Это эквивалентно вызову a.meth().
  • Оба вызова возвращают 'method', так что сравнение a.meth() == A.meth(a) возвращает True.
  • Выводится 'Equals'.

📌Правильный ответ:
3. Not Equals и Equals

Вопрос №6

Какой из вариантов позволяет создать список, содержащий элементы из двух списков, умноженные попарно, если они являются числами?

Варианты ответа:

  1. [x * y if isinstance(x, (int, float)) and isinstance(y, (int, float)) else None for x, y in zip(list1, list2)]
  2. [x * y for x, y in zip(list1, list2) if type(x) is int and type(y) is int]
  3. [x * y for x, y in zip(list1, list2) if isinstance(x, int) and isinstance(y, int)]
  4. [x * y for x, y in zip(list1, list2) if isinstance(x, (int, float)) and isinstance(y, (int, float))]
  5. [x * y for x, y in zip(list1, list2) if type(x) == int and type(y) == int]

Обоснование:

  • 1-й вариант: Он проверяет типы с помощью isinstance() и учитывает числа (целые и с плавающей точкой). Однако, он добавляет None, если элемент не проходит проверку. Это не соответствует условию задачи, так как требуется оставить только произведения чисел.
  • 2-й вариант: Используется type() для проверки на тип int. Это ограничивает проверку только целыми числами и игнорирует числа с плавающей точкой.
  • 3-й вариант: Проверка с isinstance(x, int) также ограничивает только целые числа, исключая числа с плавающей точкой.
  • 4-й вариант: Это правильный вариант, так как он проверяет, что элементы обоих списков являются числами (int или float), а затем вычисляет их произведение.
  • 5-й вариант: Аналогично второму, использует строгую проверку на int через type() и игнорирует числа с плавающей точкой.

📌Правильный ответ:
4. [x * y for x, y in zip(list1, list2) if isinstance(x, (int, float)) and isinstance(y, (int, float))]

Пояснение:
Этот вариант наиболее универсален и корректно обрабатывает все числа (целые и с плавающей точкой).

Вопрос №7

Какая процедура или функция заранее НЕ определена в модуле os?

Варианты ответа:

  1. Рекурсивное удаление директорий
  2. Редактирование прав на доступ к файлу
  3. Создание нового потока
  4. Создание символических ссылок (symlink)
  5. Создание дочернего процесса (форка)

Обоснование:

  1. Рекурсивное удаление директорий:
    В модуле os можно использовать функцию os.removedirs() для удаления директорий рекурсивно.
  2. Редактирование прав на доступ к файлу:
    Модуль os предоставляет функцию os.chmod() для изменения прав доступа к файлу.
  3. Создание нового потока:
    Модуль os не поддерживает создание потоков, так как он предназначен для работы с файловой системой и операционной системой. Для работы с потоками используется модуль threading.
  4. Создание символических ссылок (symlink):
    Модуль os предоставляет функцию os.symlink() для создания символических ссылок.
  5. Создание дочернего процесса (форка):
    В модуле os есть функция os.fork() (только на UNIX-подобных системах) для создания дочернего процесса.

📌Правильный ответ:
3. Создание нового потока

Пояснение:
Для создания потоков используется модуль threading, а не os. Модуль os не предоставляет функционала для работы с потоками.

Вопрос №8

В чем заключается главное различие между текстовым и бинарным режимами открытия файла в Python?

Варианты ответа:

  1. Текстовый режим позволяет использовать методы строк при работе с содержимым файла, а бинарный — нет.
  2. В бинарном режиме Python автоматически преобразует переносы строк, в то время как в текстовом данные читаются и записываются как есть.
  3. В текстовом режиме файлы могут быть открыты только для чтения, в то время как в бинарном — для чтения и записи.
  4. В отличие от текстового, бинарный режим поддерживает кодировку символов, что важно для международной поддержки.
  5. Бинарный режим обеспечивает меньшую скорость чтения и записи данных из-за сложности обработки содержимого по сравнению с текстовым.

Обоснование:

  • 1: Неверно. Методы строк можно применять и к текстовому содержимому, считанному из файла в бинарном режиме, после преобразования байтов в строку (bytes.decode()).
  • 2: Неверно. Наоборот, в текстовом режиме Python автоматически обрабатывает переносы строк, преобразуя их между форматами Windows (\r\n), Unix (\n) и Mac (\r). В бинарном режиме данные читаются и записываются "как есть", без изменений.
  • 3: Неверно. Режимы чтения и записи определяются флагами 'r', 'w', 'rb', 'wb' и т.д., независимо от текстового или бинарного режима.
  • 4: Неверно. Кодировка символов применяется только в текстовом режиме через параметр encoding. В бинарном режиме данные читаются и записываются в виде байтов без обработки кодировки.
  • 5: Неверно. Скорость работы зависит от объема данных и их обработки, а не от текстового или бинарного режима.

📌Правильный ответ:
2. В бинарном режиме Python автоматически преобразует переносы строк, в то время как в текстовом данные читаются и записываются как есть.

Пояснение:
Текстовый режим автоматически обрабатывает переносы строк для платформенной совместимости, тогда как в бинарном режиме этого не происходит, данные остаются неизменными.

Вопрос №9

Выберите ответ, в котором указаны только те варианты, для которых следующее выражение вернет значение True.

re.match(r"^\+7-\(\d{3,5}\)-\d{3,5}?", text) is not None

Варианты номеров телефонов:
A. +7-(123)-45678
B. +7-123-45678
C. +7-12-345678
D. +7-(1234)-5678
E. +7-12345-678

Обоснование:

  1. Регулярное выражение разбивается на части:^\+7- → строка должна начинаться с +7-.
    \(\d{3,5}\) → в скобках должно быть от 3 до 5 цифр.
    -\d{3,5}? → после тире должно быть от 3 до 5 цифр.
  2. Анализ вариантов:
    A: +7-(123)-45678
    Соответствует выражению: код в скобках содержит 3 цифры, после тире — 5 цифр. Верно.
    B: +7-123-45678
    Не соответствует, так как нет скобок вокруг первых цифр. Не верно.
    C: +7-12-345678
    Не соответствует, так как в скобках должно быть от 3 до 5 цифр, а здесь только 2. Не верно.
    D: +7-(1234)-5678
    Соответствует выражению: код в скобках содержит 4 цифры, после тире — 4 цифры. Верно.
    E: +7-12345-678
    Не соответствует, так как отсутствуют скобки вокруг первых цифр. Не верно.

📌Правильный ответ:
A, D

Пояснение:
Только варианты A и D соответствуют заданному регулярному выражению.

Вопрос №10: Выберите НЕВЕРНОЕ утверждение.

Варианты ответа:

  1. Map используется для применения функции к каждому элементу исходной последовательности, а reduce — для агрегации элементов последовательности в одно значение.
  2. Reduce больше подходит для изменения размерности последовательности.
  3. И map, и reduce принимают в качестве аргумента лямбда-функцию.
  4. Reduce возвращает массив элементов, а map — одно значение.
  5. Map больше подходит для преобразования элементов списка.

Обоснование:

  • 1. Верно. map() применяет функцию ко всем элементам последовательности, возвращая новую последовательность, а reduce() сворачивает элементы последовательности в одно значение, применяя заданную функцию.
  • 2. Неверно. reduce() не предназначен для изменения размерности последовательности. Он используется для агрегации, а не для трансформации последовательностей.
  • 3. Верно. Оба — map() и reduce() — могут принимать лямбда-функции в качестве аргумента.
  • 4. Неверно. Наоборот: reduce() возвращает одно значение (например, сумму или произведение элементов), а map() возвращает последовательность преобразованных элементов.
  • 5. Верно. map() действительно лучше всего подходит для преобразования элементов последовательности, например, для изменения формата или типов данных.

📌Правильный ответ:
2. Reduce больше подходит для изменения размерности последовательности.

Пояснение:
reduce() используется для агрегации данных, а не для изменения размерности последовательностей.

Вопрос №11

Для чего целесообразно использовать вызов с помощью конструкции raise?

Варианты ответа:

  1. Для выполнения кода, если условие истинно
  2. Для выполнения кода без обработки ошибок
  3. Для прерывания выполнения программы при возникновении ошибки
  4. Для определения функций
  5. Для итерации по элементам списка

Обоснование:

  • 1. Неверно. Конструкция raise предназначена для генерации исключений, а не для условного выполнения кода.
  • 2. Неверно. raise используется именно для явного вызова исключений, а не для пропуска обработки ошибок.
  • 3. Верно. Конструкция raise позволяет инициировать исключения, которые могут быть обработаны обработчиком (try-except) или привести к завершению программы, если обработчик отсутствует.
  • 4. Неверно. Определение функций осуществляется с помощью ключевого слова def.
  • 5. Неверно. Для итерации по элементам списка используются циклы, такие как for.

📌Правильный ответ:
3. Для прерывания выполнения программы при возникновении ошибки

Пояснение:
raise используется для генерации исключений в случаях, когда выполнение программы должно быть остановлено или перенаправлено в блок обработки ошибок.

Вопрос №12. Какой функции из модуля itertools соответствует по смыслу код ниже?

Варианты ответа:

  1. compress
  2. count
  3. takewhile
  4. cycle
  5. chain

Обоснование:

  • compress: Эта функция выбирает элементы из одной последовательности на основе значений в другой (булевый список). Это не соответствует логике данного кода.
  • count: Бесконечно генерирует числа, начиная с указанного значения. Не имеет отношения к фильтрации элементов.
  • takewhile: Эта функция из itertools выполняет ту же логику, что и данный код. Она возвращает элементы из последовательности до тех пор, пока предикат возвращает True, и прекращает выполнение после первого False.
  • cycle: Повторяет элементы последовательности бесконечно. Не имеет отношения к фильтрации.
  • chain: Соединяет несколько итераторов в один. Это также не связано с фильтрацией.

📌Правильный ответ:
3. takewhile

Пояснение:
takewhile(predicate, iterable) возвращает элементы последовательности до тех пор, пока выполняется предикат, а затем прекращает итерацию, что полностью совпадает с логикой указанного кода.

Вопрос №13. Какой из вариантов содержит ОШИБКУ при использовании итератора?

Варианты ответа:

  1. list(iter([4, 5, 6]))
  2. next(iter([1, 2, 3]))
  3. iter([1, 2, 3])[0]
  4. [x for x in iter([1, 2, 3])]
  5. sum(iter([1, 2, 3]))

Обоснование:

  • 1. list(iter([4, 5, 6])): Создает итератор из списка [4, 5, 6], а затем преобразует итератор обратно в список. Ошибки нет.
  • 2. next(iter([1, 2, 3])): Создает итератор из списка [1, 2, 3] и возвращает первый элемент с помощью next(). Ошибки нет.
  • 3. iter([1, 2, 3])[0]: Создает итератор из списка [1, 2, 3]. Однако итераторы в Python не поддерживают индексацию, поэтому возникает ошибка TypeError.
  • 4. [x for x in iter([1, 2, 3])]: Создает список через генератор, проходя по итератору. Ошибки нет.
  • 5. sum(iter([1, 2, 3])): Итератор используется для суммирования элементов. Ошибки нет.

📌Правильный ответ:
3. iter([1, 2, 3])[0]

Пояснение:
Итераторы не поддерживают индексацию. Попытка обращения к элементу через индекс ([0]) вызывает ошибку TypeError.

Вопрос №14: Что выводит этот код?

Варианты ответа:

Обоснование:

  1. Код вызывает функцию recursive_print с аргументом n=3.
  2. Первая итерация:n=3, так как n > 0, выполняется print(3).
    Затем вызывается recursive_print(2).
  3. Вторая итерация:n=2, так как n > 0, выполняется print(2).
    Затем вызывается recursive_print(1).
  4. Третья итерация:n=1, так как n > 0, выполняется print(1).
    Затем вызывается recursive_print(0).
  5. Четвертая итерация:n=0, так как n <= 0, выполняется print("Done").

Код завершает выполнение после вывода строки "Done".

📌Правильный ответ:
1.

3
2
1
Done

Пояснение:
Код использует рекурсию для последовательного вывода чисел от n до 1, а затем выводит "Done" при достижении n=0.

Вопрос №15

Какой из методов Python используется для эффективного управления использованием памяти при работе с большими наборами данных, обеспечивая как возможность итерации, так и снижение использования памяти?

Варианты ответа:

  1. dictionary comprehension
  2. regular function
  3. set comprehension
  4. generator expression
  5. list comprehension

Обоснование:

  • 1. dictionary comprehension: Создает словарь, используя аналогичную синтаксическую конструкцию, как в списочных выражениях. Однако словари полностью хранятся в памяти и не подходят для работы с большими данными.
  • 2. regular function: Обычные функции не являются инструментом управления памятью. Они могут возвращать большие структуры данных, что приводит к значительным затратам памяти.
  • 3. set comprehension: Аналогично list comprehension, создает множество, которое полностью загружается в память, что не подходит для экономии памяти при работе с большими наборами данных.
  • 4. generator expression: Создает генератор, который вычисляет значения "лениво" (по мере необходимости), не загружая все данные в память. Это делает его идеальным инструментом для обработки больших наборов данных с минимальным использованием памяти.
  • 5. list comprehension: Создает список, который полностью хранится в памяти. Это менее эффективно при работе с большими данными.

📌Правильный ответ:
4. generator expression

Пояснение:
Генераторные выражения позволяют обрабатывать элементы по одному, что снижает использование памяти по сравнению с другими структурами данных, такими как списки или словари.

Вопрос №16
Какое утверждение о декораторе lru_cache НЕВЕРНО?

Варианты ответа:

  1. lru_cache можно использовать для мемоизации рекурсивных функций.
  2. По умолчанию lru_cache предназначен для кэширования результатов функции в пределах одного процесса Python.
  3. При использовании lru_cache первыми из кэша вытесняются элементы, неиспользованные дольше всех.
  4. lru_cache основан на алгоритме Least Recently Used.
  5. По умолчанию у lru_cache задано максимальное количество элементов в кэше, равное 256.

Обоснование:

  • 1. Верно. lru_cache идеально подходит для мемоизации рекурсивных функций, поскольку он запоминает результаты ранее выполненных вызовов, что ускоряет выполнение при повторных вызовах.
  • 2. Верно. Кэш, созданный с помощью lru_cache, работает только в пределах одного процесса Python.
  • 3. Верно. Алгоритм Least Recently Used (LRU) вытесняет из кэша самые старые элементы, которые дольше всего не использовались.
  • 4. Верно. lru_cache действительно реализует алгоритм LRU для управления кэшем.
  • 5. Неверно. По умолчанию у lru_cache максимальное количество элементов в кэше составляет 128, а не 256.

📌Правильный ответ:
5. По умолчанию у lru_cache задано максимальное количество элементов в кэше, равное 256.

Пояснение:
Значение по умолчанию для параметра maxsize в lru_cache — 128, но его можно изменить при вызове декоратора.

👉🏻Навигация и ссылки по всем материалам в Telegram

Заключение

Дорогие читатели! Если материалы данной статьи помогли вам успешно пройти тест на платформе, буду признателен, если вы поставите лайк 👍🏻 именно той статье, которая соответствовала вашему уровню подготовки. Также, если тестирование оказалось неудачным ❌, пожалуйста, оставьте комментарий 📝 с указанием количества ошибок допущенных в тесте.

Эта обратная связь чрезвычайно важна. Она позволит в дальнейшем проанализировать эффективность материалов, а также создать аналитическое заключение для всей серии статей по прохождению тестирования на платформе. Спасибо за вашу помощь в совершенствовании контента!