Списки в Python: 11 вопросов, которые могут задать на собеседовании
Собеседование без списков — время на ветер. Рассказываем главное, что надо о них знать.


vlada_maestro / shutterstock
Список — базовая структура данных в Python. На собеседовании на позицию младшего Python-разработчика речь о нём зайдёт практически наверняка.
Мы выбрали самые популярные вопросы с собеседований, касающиеся списков, и оценили их сложность в эмодзи: (◡‿◡), (ー_ー) и (> ⌒ <). В большинстве случаев в качестве ответа достаточно написать код и прокомментировать его.
Если на вашем компьютере нет интерпретатора Python, можно воспользоваться онлайн-сервисами: Repl.it, Python Fiddle, CodeChef и другими — в них можно кодить прямо из браузера. Просто выберите язык Python версии 3.* и запускайте примеры на исполнение.
Главное, что нужно помнить о списках
- Список — это упорядоченный набор элементов, перечисленных через запятую, заключённый в квадратные скобки.
- Элементы списка могут быть разных типов, в отличие от элементов массива (array), но, как правило, используются списки из элементов одного типа.
- Список может содержать одинаковые элементы, в отличие от множества (set).
- Список можно изменить после создания, в отличие от кортежa (tuple).
- Список может содержать другие списки.
Вопрос 1. Как объединить списки?
Сложность: (◡‿◡)
Проще всего списки объединяются с помощью сложения +.
a = [1, 2]
b = ['a', 'c']
print(a + b)
>>> [1, 2, 'a', 'c']
print(b + a)
>>> ['a', 'c', 1, 2]
Как видно, итоговый список будет зависеть от порядка слагаемых. Исходные списки остаются неизменными.
Также списки можно объединить с помощью функции extend (). О ней ниже.
Вопрос 2. Как умножать списки?
Сложность: (◡‿◡)
Списки можно умножать на целое число (тип int), исходный список при этом не меняется. Умножение списка на другие типы данных, в том числе и на другой список, вызовет ошибку.
Умножим список [1, 2, 'b'] на 2:
list_a = [1, 2, 'b'] * 2
print(list_a)
>>> [1, 2, 'b', 1, 2, 'b']
Содержание списка [1, 2, 'b'] повторилось дважды. Если умножить список на 0 или отрицательное число, то получим пустой список.
Вопрос 3. Как проверить, существует ли значение в списке?
Сложность: (◡‿◡)
Для проверки используем in:
'a' in ['a', 'c', 'd', 'e', 'b', 'c']
>>> True
3 in ['a', 'c', 'd', 'e', 'b', 'c']
>>> False
В этом примере in возвращает True, когда элемент в списке есть, и False — если нет.
Вопрос 4. Как перевернуть список?
Сложность: (◡‿◡)
Список переворачивается с помощью функции .reverse (). При этом она меняет исходный список.
list_1 = ['a', 'b', 'c', 'd', 'e']
print(list_1)
>>> ['a', 'b', 'c', 'd', 'e']
list_1.reverse()
print(list_1)
>>> ['e', 'd', 'c', 'b', 'a']
Сначала создали список list_1, затем вывели его на экран. Потом перевернули список с помощью .reverse () и вывели на экран уже изменённый список.
Вопрос 5. В чём разница между append и extend?
Сложность: (ー_ー)
Если кратко, то append () добавляет в конец списка значения поодиночке, а extend () добавляет их же и туда же, но уже списками.
Как работает append ():
list_a = ['a', 'b', 'c'] # создали первый список
list_a.append('d')
print(list_a)
>>> ['a', 'b', 'c', 'd']
С помощью функции append () мы добавили одиночное значение 'd' к первому списку list_a и в следующей строке вывели получившийся список на экран. Как видим, в конец списка добавился ещё один элемент 'd'.
Как работает extend ():
list_b = [1, 2, 3] # создали второй список
list_b.extend(list_a)
print(list_b)
>>> [1, 2, 3, 'a', 'b', 'c', 'd']
Мы применили ко второму списку list_b функцию extend (), аргументом у которой стал другой список, list_a из кода выше. Вывод показал, что оба списка слились в один и стали новым списком list_b.
Тот же результат будет, если сложить два списка (list_b + list_a) и присвоить результат переменной list_b. Сможете написать код самостоятельно?
Вопрос 6. Как удалить из списка дубликаты?
Сложность: (ー_ー)
Это можно сделать путём преобразования списка во множество (set, иногда ещё его называют «набор»), а затем обратно в список:
list_b = [1, 1, 3, 1, 4, 8, 4] # список с дубликатами
list_b = list(set(list_b)) # здесь основная магия
print(list_b)
>>> [8, 1, 3, 4]
С помощью функции set () преобразовали список list_b во множество, затем тут же, в этой же строке, обратно в список (с помощью уже знакомой нам функции list), и присвоили полученный результат той же переменной list_b. Python очень гибкий язык!
Здесь используется одно из свойств множества: в нём могут быть только уникальные элементы. Обратите внимание, что порядок следования элементов исходного списка (тех, что остались) может не сохраниться.
Вопрос 7. Преобразуйте цикл for в генератор списков
Сложность: (ー_ー)
Питонисты очень любят решения в одну строчку. Цикл for содержит минимум две строки, поэтому здесь есть где развернуться.
Дан следующий цикл for:
a = [1, 2, 3, 4, 5] # первый список, по которому бежим
a2 = [] # пустой список, который надо заполнить
for i in a:
a2.append(i + 1) # заполняем его в цикле for
print(a2)
>>> [2, 3, 4, 5, 6]
Пока i бежит по первому списку a, цикл заполняет второй список значениями на единицу больше текущего значения i. Итого четыре строки, не считая вывода итогового списка.
Как сделать то же самое, но с помощью генератора списка:
a = [1, 2, 3, 4, 5] # первый список, по которому бежим
a3 = [i+1 for i in a] # генератор и сразу итоговый список
print(a3)
>>> [2, 3, 4, 5, 6]
Получили точно такой же список, но уже за две строки и без манипуляций с функцией append () в теле цикла for. Генератор списка принято считать более каноническим способом в Python, если он остаётся понятным.
Вопрос 8. В чём разница между remove, pop и del?
Сложность: (ー_ー)
Каждый из этих трёх методов (точнее, двух методов и одной команды) удаляет элементы списка. Но каждый делает это по-своему и, соответственно, применяется в разных ситуациях.
remove ()
Метод remove () удаляет из списка первое совпадающее значение.
Возьмём список и удалим из него элемент 'b':
list_1 = ['a', 'b', 'c', 'd', 'e', 'b', 'c']
list_1.remove('b')
print(list_1)
>>> ['a', 'c', 'd', 'e', 'b', 'c']
Первая 'b' исчезла, но вторая 'b' осталась в списке.
pop ()
Метод pop () удаляет элемент по индексу и возвращает этот элемент:
list_3 = ['a','b','c','d']
list_3.pop(2)
>>> 'c'
print(list_3)
>>> ['a', 'b', 'd']
Индексация в Python идёт с нуля, поэтому элемент с индексом 2 — третий по счёту. В последних строчках мы вывели изменённый список.
del
Команда del тоже удаляет элемент списка по его индексу, но имеет отличный от pop () синтаксис и ничего не возвращает:
list_1 = ['a', 'c', 'd', 'e', 'b', 'c']
del list_1[4]
print(list_1)
>>> ['a', 'c', 'd', 'e', 'c']
Мы недосчитались четвёртого по индексу (и пятого по счёту) элемента, то есть 'b'.
Также команда del может удалять из списка срезы (slices):
list_2 = [-1, 1, 66.25, 333, 337, 1234.5]
del list_2[2:4]
print(list_2)
>>> [-1, 1, 337, 1234.5]
При указании границ среза в Python последний элемент в срез не входит. Поэтому из списка удалены элементы с третьего (индекс 2) по пятый (индекс 4), исключая последний, то есть 66.25 и 333.
Наконец, del может удалять целые переменные.
list_2 = [-1, 1, 66.25, 333, 333, 1234.5]
print(list_2)
>>> [-1, 1, 66.25, 333, 333, 1234.5]
del list_2
print(list_2)
Traceback (most recent call last):
File "<pyshell#55>", line 1, in <module>
list_2
NameError: name 'list_2' is not defined
Сначала создали список, затем вывели его на экран, чтобы убедиться, что он существует. Потом применили к нему команду del и вызвали снова. Ошибка! Python забыл, что вообще была такая переменная list_2.
Вопрос 9. Чем список отличается от других структур?
Сложность: (> ⌒ <)
Такие вопросы надо отбивать особенно чётко. Если спрашивающий не услышит конкретные ключевые слова, его подозрительность повысится, а ваши шансы, наоборот, снизятся.
Список и кортеж (tuple)
Список можно менять после создания (например, с помощью функции append ()), а кортеж нет: он защищает данные от изменений после создания. По этой причине кортеж можно использовать в качестве ключа в словарях, а список нельзя. Кроме того, кортеж обрабатывается интерпретатором Python чуть быстрее.
Список и множество (set)
Список упорядочен: каждый элемент списка имеет индекс, а элемент множества — нет. Список может содержать одинаковые значения, а во множестве каждое значение уникально. Проверка, принадлежит ли элемент множеству, выполняется быстрее, чем такая же проверка элемента списка.
Список и словарь (dictionary)
Словарь состоит из пар «ключ-значение», а список может состоять и из одиночных элементов, и из пар, и из троек — если элементами будут другие списки или кортежи. Ключи у словаря должны быть уникальными и иметь неизменяемый тип, у списка таких ограничений нет. Поиск по словарю быстрее, чем по списку.
Список и массив (array)
Для использования массива нужно вызывать библиотеку array, а списки встроены в Python. В массиве могут содержаться элементы только одного типа. Массив не может содержать другие массивы или списки. Массив занимает меньше памяти и поэтому быстрее, чем одномерный список.
Список и массив NumPy (numpy.array)
Всё то же самое, что и в предыдущем пункте, только нужно вызывать библиотеку numpy. Плюс отличие в арифметических действиях: например, сложение не объединяет массивы, а применяется к их элементам по правилам линейной алгебры.
Вопрос 10. Как объединить два списка в список кортежей?
Сложность: (> ⌒ <)
Для объединения двух списков в список кортежей можно использовать функцию zip, причём не только для двух, но и для трёх и более списков. Это полезно для формирования, например, матриц из векторов.
list_a = ['a' ,'b' ,'c', 'd']
list_b = [1, 2, 3, 4]
list_c = [(k, v) for k, v in zip(list_a, list_b)])
print(list_c)
>>> [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
В первых двух строчках мы создали два списка, которые надо объединить. В третьей с помощью конструкции, похожей на двойной генератор, создали список, состоящий из кортежей вида (k, v), где k и v берутся из двух наших списков с помощью функции zip (). К слову, она не зря носит такое название: в переводе zip означает «застёжка-молния», и эта функция как бы сшивает два списка в один.
Не забудьте проверить, что случится, если списки будут разной длины, и можно ли получить не список кортежей, а список списков.
Вопрос 11. Как работает функция range?
Сложность: (ー_ー)
Функция range () генерирует три разных вида последовательностей из целых чисел и часто используется для быстрого создания списков — поэтому этот вопрос и попал в нашу подборку. Да и объяснять работу функции удобнее всего именно с помощью списка.
Последовательность от нуля до n
Используется range (n):
list_a = [i for i in range(10)] # первый способ
print(list_a)
>>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list_b = list(range(10)) # второй способ
print(list_b)
>>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Функция range (n) сгенерировала последовательность от нуля до n (исключая n), а мы эту последовательность двумя способами обернули в список. Первый способ вы уже узнали — это генератор списков, а второй использует функцию list, которая превращает подходящий аргумент в список.
Попробуйте передать в range () отрицательное (-7) или дробное (3.14) число. Получится ли какой-нибудь список из этого, и если да, то какой?
Последовательность от n до m
Здесь в функцию range () нужно передать уже два аргумента: тогда range (n, m) сгенерирует целые числа от n до m (исключая m):
list_a = [i for i in range(3, 12)] # первый способ
print(list_a)
>>> [3, 4, 5, 6, 7, 8, 9, 10, 11]
list_b = list(range(9, 20)) # второй способ
print(list_a)
>>> [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Последовательность от n до m с шагом k
Если в функцию range () передать три аргумента n, m, k, то она снова создаст последовательность от n до m (снова исключая m), но уже с шагом k:
list_a = [i for i in range(2, 34, 4)] # первый способ
print(list_a)
>>> [2, 6, 10, 14, 18, 22, 26, 30]
list_b = list(range(2, 34, 4)) # второй способ
print(list_b)
>>> [2, 6, 10, 14, 18, 22, 26, 30]
Разница между элементами (шаг) равна третьему аргументу, то есть 4. Так как последний элемент (34) исключён, то список у нас заканчивается на 30.
Дайте мне список, и я переверну мир
Так (или примерно так) говорил ещё Архимед, а кто мы такие, чтоб с ним спорить. Список — простой, понятный и надёжный инструмент: в любой непонятной ситуации попробуйте сначала применить список, и даже если он не подойдёт, то подскажет, как и чем решать задачу дальше. Обязательно посмотрите другие методы списков из официальной документации Python, чтобы они не оказались для вас сюрпризом на собеседовании.
Конечно, Python — это не только списки, и изучать его лучше на родном языке в компании единомышленников. Приходите на наш курс «Профессия Python-разработчик». Под руководством опытных наставников вы станете настоящим укротителем питонов повелителем списков, массивов и словарей, а заодно получите востребованную и высокооплачиваемую специальность.