150 вопросов для собеседования на вакансию Python. Часть 2 (с опытом работы)

Вторая часть перевода с английского материала с крупного портала по обучению Python. Часть 1 была посвящена обзору тех категорий вопросов, которые чаще задаются новичкам без опыта работы, желающим занять вакансию для работы с Python. Часть 2 содержит более сложные и каверзные вопросы, которые могут быть заданы более опытным программистам.

Источник: 71 Python Interview Questions and Answers [New] – Get Ready for Technical Round

Часть первая: 150 вопросов для собеседования на вакансию Python. Часть 1 (без опыта работы).

В1. Когда в блоке try-except исполняется элемент else?

В блоке if-else элемент else исполняется в случае, если условие в операторе if (if statement) является неверным (False). А вот в блоке try-except элемент else исполняется только в случае, если элемент try не выдает исключение.

В2. Допустим, есть список nums=[0,1,2,3,4]. Что означает nums[-1]?

Данный код не будет выдавать исключение. nums[-1] — это 4, потому что движение по элементам начинается справа.

В3. Что такое переменная PYTHONPATH?

PYTHONPATH — эта переменная сообщает интерпретатору путь до файлов модуля, импортированных в программу. Поэтому она должна включать в себя директорию с библиотекой-источником питона и директории с исходным кодом питона. Переменную PYTHONPATH можно назначить самостоятельно, однако обычно ее предустанавливает установщик питона.

В4. Расскажите про функции join() и split() в Python.

Функция join() позволяет соединять символы строки (string), чередуя с указанным символом.

>>> ','.join('12345')
‘1,2,3,4,5’

Функция split() позволяет разделить строку, чередуя символы с указанным символом.

>>> '1,2,3,4,5'.split(',')
[‘1’, ‘2’, ‘3’, ‘4’, ‘5’]

В5. Расскажите, какая будет выдача у этого кода:

x=[‘ab’,’cd’]
print(len(map(list,x)))

Здесь будет ошибка определения типа (TypeError). Ее причиной является отсутствие атрибута len() у функции map(). Это можно проверить функцией dir().

В6. Приведите несколько методов, с помощью которых можно реализовать в питоне функционально ориентированное программирование.

Несколько методов могут помочь с итерацией по списку (list).

1. filter() может отфильтровать несколько значений на основе условия.

>>> list(filter(lambda x:x>5,range(8)))
[6, 7]

2. map() применяет функцию к каждому элементу итерируемого объекта.

>>> list(map(lambda x:x**2,range(8)))
[0, 1, 4, 9, 16, 25, 36, 49]

3. reduce() продолжает уменьшать последовательность (sequence) парами, пока не будет достигнуто единичное значение.

>>> from functools import reduce
>>> reduce(lambda x,y:x-y,[1,2,3,4,5])
-13

В7. И все-таки, какая выдача у этого кода:

x=[‘ab’,’cd’]
print(len(list(map(list,x))))

Здесь на выходе ‘2’, потому что длина этого списка составляет два элемента. Выдача у list(map(list,x)) будет [[‘a’, ‘b’], [‘c’, ‘d’]], а в этом списке два элемента.

В8. Можно ли сказать, что del и remove() — это одно и то же? Что это такое, в целом?

del и remove() — это методы для списков, они нужны для удаления элементов.

>>> list=[3,4,5,6,7]
>>> del list[3]
>>> list
[3, 4, 5, 7]
>>> list.remove(5)
>>> list
[3, 4, 7]

del позволяет удалять элементы под конкретным индексом, а remove() позволяет удалять элементы на основе их значения.

В9. Как нужно открывать файл для записи?

Допустим, есть файл tabs.txt. Чтобы открыть его и записывать в него, необходим такой код:

>>> file=open('tabs.txt','w')

Теперь файл открылся в режиме записи. После завершения работы файл нужно закрыть.

>>> file.close()

В10. Объясните, почему у следующего кода такая выдача:

>>> tuple=(123,'John')
>>> tuple*=2
>>> tuple
(123, ‘John’, 123, ‘John’)

В этом коде кортеж (tuple) умножается на 2. Поэтому его содержимое удваивается. То есть, на выходе мы получим (123, ‘John’, 123, ‘John’). Со строками тоже можно так сделать:

>>> 'ba'+'na'*2
‘banana’

В11. Какие различия есть между методами для списков append() и extend()?

Метод append() добавляет элемент к концу списка, а метод extend() добавляет к концу списка переданный ему итерируемый объект (iterable). Возьмем два списка.

>>> list1, list2 = [1, 2, 3], [5, 6, 7, 8]

Вот так действует append():

>>> list1.append(4)
>>> list1
[1, 2, 3, 4]

А вот так действует extend():

>>> list1.extend(list2)
>>> list1
[1, 2, 3, 4, 5, 6, 7, 8]

В12. Какие есть режимы обработки файлов в Python?

Предусмотрены следующие режимы:

  • только чтение – ‘r’
  • только запись – ‘w’
  • чтение-запись – ‘rw’
  • добавление в конце – ‘a’

Можно открыть текстовый файл с опцией ‘t’. Поэтому, чтобы открыть текстовый файл для чтения, можно использовать режим ‘rt’. Точно так же для бинарных файлов используется ‘b’.

В13. Что делает функция map()?

Функция map() возвращает итератор, который применяет функцию, переданную ей в первом аргументе, ко всем элементам итерируемого объекта (iterable), переданного ей во втором аргументе. Можно показать пример?

>>> for i in map(lambda i:i**3, (2,3,7)):
....print(i)
8
27
343

На выходе – элементы 2, 3, 7, возведенные в куб.

В14. Расскажите про try, raise и finally.

Это ключевые слова (keywords) для обработки исключений (exception handling). Потенциально рискованный код помещается в блок try, оператор raise (raise statement) используется для прямого вызова ошибки, а в блоке finally находится код, который исполняется в любом случае.

В15. Что случится, если не обработать ошибку в блоке except?

Если этого не сделать, программа завершится. Затем она отправит трассу исполнения на sys.stderr.

В16. Есть ли возможность удалить последний объект списка?

Да, такая возможность предусмотрена. Можно попробовать такой вариант:

>>> list=[1,2,3,4,5
>>> list.pop(-1)
5
>>> list
[1, 2, 3, 4]

В17. Как можно преобразовать целое число (integer) в символ Unicode?

Для этого просто нужна встроенная функция chr(x). Можно показать?

>>> chr(52)
‘4’
>>> chr(49)
‘1’
>>> chr(67)
‘C’

В18. Объясните, какая проблема с этим кодом:

>>> def func(n=[]):
#playing around
pass
>>> func([1,2,3])
>>> func()
>>> n

В результате запроса n появляется ошибка присвоения названия (NameError), потому что n является локальной переменной функции func. В другом месте она недоступна. Также Python определяет значения параметров по умолчанию только один раз, поэтому каждый вызов функции использует то же значение по умолчанию. Если во время какого-то вызова будет изменено значение по умолчанию, то в следующем вызове будет использовано новое значение.

В19. Что здесь написано?

s = a + ‘[’ + b + ‘:’ + c + ‘]’

Здесь выполняется сцепление строк (string concatenation). Если a, b и c являются строками (strings), то все пройдет нормально, они будут сцеплены со строками ‘[’, ‘:’ и ‘]’. Однако, если хотя бы один из элементов сцепления не является строкой, то появится ошибка TypeError.

В20. Может ли рекурсия создавать сложности?

Разумеется:

  • Приходится чаще вызывать функцию.
  • Каждый вызов функции сохраняет переменную состояния в программном стеке, то есть растет потребление памяти, что в итоге может стать причиной переполнения памяти.
  • Вызовы функции отнимают время.

В21. Какие преимущества у рекурсии?

Рекурсия помогает:

  • экономить усилия на выполнение задачи,
  • сократить объем кода по сравнению с циклами,
  • легче воспринимать код.

В22. Какой выход у этого кода?

>>> b=(1)

На выходе не будет кортежа (tuple). Мы получим обычное целое число.

>>> type(b)

Чтобы получить кортеж, мы можем добавить прямую декларацию с помощью запятой после числа 1:

>>> b=(1,)
>>> type(b)

В23. Почему игнорируются имена-идентификаторы, которые начинаются с символа подчеркивания?

В питоне не реализована концепция скрытой переменной (private variable), поэтому принято декларировать скрытые переменные первым символом в виде нижнего подчеркивания.

В24. Можно ли удалить пробелы из строки (string) “aaa bbb ccc ddd eee”?

Я вспомнил три способа.

Функция join():

>>> s='aaa bbb ccc ddd eee'
>>> s1=''.join(s.split())
>>> s1
‘aaabbbcccdddeee’

Генератор списка (list comprehension):

>>> s='aaa bbb ccc ddd eee'
>>> s1=str(''.join(([i for i in s if i!=' '])))
>>> s1
‘aaabbbcccdddeee’

Функция replace():

>>> s='aaa bbb ccc ddd eee'
>>> s1 = s.replace(' ','')
>>> s1
‘aaabbbcccdddeee’

В25. Как узнать текущую директорию в питоне?

Чтобы узнать, в какой директории мы сейчас находимся, можно использовать метод getcwd() из модуля os module.

>>> import os
>>> os.getcwd()
‘C:\\Users\\Ayushi\\AppData\\Local\\Programs\\Python\\Python37-32’

В26. Как можно перемешать в случайном порядке (рандомизировать) содержание списка (list) путем его изменения?

Для этого можно импортировать функцию shuffle() из модуля random.

>>> from random import shuffle
>>> shuffle(mylist)
>>> mylist
[3, 4, 8, 0, 5, 7, 6, 2, 1]

В27. Если строка (string) начинается с пробела, как его убрать?

Такой пробел можно убрать с помощью метода lstrip().

>>> ' Ayushi '.lstrip()‘
‘Ayushi '

В этой строке пробелы стояли как в начале, так и в конце. Функция lstrip() убрала крайний слева пробел из строки. Если мы захотим убрать пробел из хвоста, то воспользуемся функцией rstrip().

>>> ' Ayushi '.rstrip()
‘ Ayushi’

В28. Сейчас мы покажем код, в котором нужно удалить числа меньше 5 из списка (list) nums. Однако, ожидаемого эффекта он не дает. Можете показать нам, где баг?

>>> nums=[1,2,5,10,3,100,9,24]
>>> for i in nums:
....if i<5:
........nums.remove(i)
>>> nums
[2, 5, 10, 100, 9, 24]

В данном коде проверяется каждый элемент списка nums, т.е. окажется ли он меньше 5. Если условие выполняется, то данный элемент будет удален. На первой итерации, действительно, оказывается, что 1 меньше 5, поэтому данный элемент удаляется из списка. Однако данное действие вносит путаницу в индексы элементов, поэтому программа проверяет элемент 5, а не элемент 2. Решить данную проблему можно тремя способами:

Можно создать пустой массив и добавлять элементы с конца (append):

>>> nums=[1,2,5,10,3,100,9,24]
>>> newnums=[]
>>> for i in nums:
if i>=5:
newnums.append(i)
>>> newnums
[5, 10, 100, 9, 24]

Можно воспользоваться генератором списка (list comprehension):

>>> nums=[1,2,5,10,3,100,9,24]
>>> newnums=[i for i in nums if i>=5]
>>> newnums
[5, 10, 100, 9, 24]

Можно воспользоваться функцией filter():

>>> nums=[1,2,5,10,3,100,9,24]
>>> newnums=list(filter(lambda x:x>=5, nums))
>>> newnums
[5, 10, 100, 9, 24]

В29. Что за функция enumerate() в Python?

Функция enumerate() осуществляет итерацию вдоль последовательности (sequence), извлекает индекс и его значение.

Посмотрим на примере.

>>> for i,v in enumerate(['Python','C++','Scala']):
....print(i,v)
0 Python
1 C++
2 Scala

В30. Как можно создать такой паттерн в питоне?

*
**
***
****
*****

Можно использовать два цикла for (for-loops).

>>> for i in range(1,6):
....for j in range(1,i+1):
........print('*',end='')
....print()

В31. В каком случае while уместнее, чем for?

В целом, for подойдет во всех случаях, когда применим while, однако есть несколько ситуаций, когда с циклом while проще:

  • Простые повторяющиеся циклы
  • Когда не нужно осуществлять итерацию вдоль списка элементов (например, записи в базе данных и символы строки.

В32. Посмотрим на вот такой код:

>>> A0=dict(zip(('a','b','c','d','e'),(1,2,3,4,5)))
>>> A1=range(10)
>>> A2=sorted([i for i in A1 if i in A0])
>>> A3=sorted([A0[s] for s in A0])
>>> A4=[i for i in A1 if i in A3]
>>> A5={i:i*i for i in A1}
>>> A6=[[i,i*i] for i in A1]
>>> A0,A1,A2,A3,A4,A5,A6

Какие будут значения у переменных с A0 по A6? Объясните свой ответ.

Результат будет следующий:

A0={‘a’: 1, ‘b’: 2, ‘c’: 3, ‘d’: 4, ‘e’: 5}
A1=range(0, 10)
A2=[]
A3=[1, 2, 3, 4, 5]
A4=[1, 2, 3, 4, 5]
A5={0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
A6=[[0, 0], [1, 1], [2, 4], [3, 9], [4, 16], [5, 25], [6, 36], [7, 49], [8, 64], [9, 81]]

Теперь посмотрим, что тут произошло. A0 сцепляет ‘a’ с 1, ‘b’ с 2 и т.д. с помощью функции zip(). Создаются кортежи (tuples), которые затем передаются функции dict() и преобразовываются в словарь, в котором ключами (keys) и значениями (values) являются элементы кортежей.

A1 создает объект range, который начинается на start=0 и заканчивается на stop=10.

A2 проверяет каждый элемент в A1 на его присутствие в A0. Если ответ утвердительный, элемент добавляется в список (list). Затем этот список упорядочивается (сортируется) с помощью sorted(). Поскольку нет ни одного элемента, присутствующего и в A0, и в A1, на выходе получаем пустой список.

A3 берет каждый ключ в A0 и возвращает его значение. В итоге мы получаем список [1,2,3,4,5].

A4 проверяет каждый элемент A1 на его присутствие в A3. Если ответ утвердительный, элемент добавляется в список, который мы получаем на выходе.

A5 возводит каждый элемент A1 в квадрат и возвращает словарь (dictionary), в котором ключами являются элементы A1 и они же, возведенные в квадрат, являются значениями.

A6 берет каждый элемент A1 и возвращает вложенные списки (sublists) с этими элементами и их квадратами. По одному.

В33. Есть ли в питоне оператор переключения (switch-case statement)?

В питоне такого оператора нет, но можно написать функцию для реализации этого функционала. Как вариант, можно использовать набор операторов if-elif-else. В такой функции может использоваться словарь (dictionary).

>>> def switch(choice):
....switcher={'Ayushi':'Monday', 'Megha':'Tuesday'}
....print(switcher.get(choice,'Hi, user'))
>>> switch('Megha')
Tuesday
>>> switch('Ayushi')
Monday
>>> switch('Ruchi')
Hi, user

В этом примере метод get() возвращает значение (value) для ключа (key). Если нет нужного ключа, возвращается значение, заданное по умолчанию (второй аргумент метода get()).

В34. Объясните разницу между полной копией (deep copy) и поверхностной копией (shallow copy).

Полное копирование создает новый объект-копию. То есть, если внести изменение в копию объекта, то с первоначальным объектом ничего не случится. В Python для этого используется функция deepcopy() с помощью импорта из модуля copy.

>>> import copy
>>> b=copy.deepcopy(a)

Поверхностная копия копирует на новый объект ту ссылку, которая закреплена на первоначальном объекте. Поэтому если внести изменение в копию, то оно распространится на первоначальный объект. Данный функционал реализуется с помощью функции copy().

>>> b=copy.copy(a)

В35. Можно ли создать локальную переменную (local variable), имя которой начинается с символа нижнего подчеркивания?

Можно, но не рекомендуется. Локальными переменными обозначаются скрытые переменные (private variables) класса, поэтому такое действие запутает интерпретатора (interpreter).

В36. Можно ли сказать, что массив (array) NumPy лучше списка (list)?

Массивы NumPy имеют три преимущества перед списками:

  • Они быстрее
  • Они потребляют меньше памяти
  • С ними удобнее работать

В37. Если установить модуль с помощью pip, но импортировать его в интегрированную среду разработки (IDLE) не получается. В чем может быть причина?

  • Возможно, в системе установлена более чем одна версия питона. Например 32- и 64-битную.
  • Переменная пути (Path variable) в списке переменных системного окружения (system environment variable) может быть назначена на обе версии, но с приоритетом одной из них, например 32-битной.
  • Получается, что, включая приглашение командной строки (command prompt), я использую 32-битную версию pip для установки модуля.
  • При запуске интегрированной среды разработки используеются 64-битная версия.

Если все произойдет вот так, то импортировать установленный модуль не получится.

В38. Отталкиваясь от предыдущего вопроса и ответа, как можно решить данную проблему?

Есть два варианта.

1. Временное решение. Можно добавлять путь вручную через sys перед каждым включением новой сессии интерпретатора (interpreter).

>>> sys.path.append('C:\\Users\\Ayushi\\AppData\\Local\\Programs\\Python\\Python37\\Scripts')

2. Постоянное решение. Обновить значение Path в переменных окружения, чтобы папка Scripts 64-битной версии была первой.

В39. Допустим, во время установки пакета (package) с помощью pip появляется ошибка "No matching installation found" (Подходящей установки не обнаружено). Что здесь можно сделать?

В такой ситуации можно, как вариант, загрузить исполняемые файлы пакета.

В40. Как можно отслеживать разные версии кода?

Для этого используется контроль версий (version control). Одним из возможных инструментов контроля является Git.

В41. Как можно провести отладку (debug) программы в Python? Дайте короткий ответ.

Для отладки в Python используется модуль pdb, отладчик (debugger) Python. Если запустить программу с pdb, то мы сможем пройти по коду пошагово.

В42. Можно ли осуществить динамическую загрузку модуля в Python?

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

import importlib
module = importlib.import_module('my_package.my_module')

В43. Какие методы/функции мы используем для определения типа экземпляра (type of instance) и наследования (inheritance)?

Для этого используются type(), isinstance() и issubclass().

1. type() используется для определени типа объекта.

>>> type(3)
>>> type(False)
>>> type(lambda :print("Hi"))
>>> type(type)

2. isinstance() принимает два аргумента: значение (value) и тип (type). Если значение относится к соответствующему типу, то возвращается True. Если нет, то возвращается False.

>>> isinstance(3,int)
True
>>> isinstance((1),tuple)
False
>>> isinstance((1,),tuple)
True

3. issubclass() принимает два класса (classes) в качестве аргументов (arguments). Если второй наследует из первого, то возвращается True. Если нет, то возвращается False.

>>> class A: pass
>t; class B(A): pass
>>> issubclass(B,A)
True
>>> issubclass(A,B)
False

В44. Методы (methods) и конструкторы (constructors) — это одно и то же или нет?

Разница между ними очень тонкая, но важная:

  • Название конструктора должно соответствовать названию класса, а метод можно называть как угодно.
  • Конструктор исполняется при создании объекта, а метод исполняется при его вызове.
  • Конструктор исполняется один раз для каждого объекта, а метод можно вызывать по одному объекту неограниченно.
  • Конструкторы используются для определения (define) и инициализации не статических переменных. Методы используются для осуществления операций в рамках бизнес-логики.

В45. Что понимается под модулем в питоне?

Модуль — это скрипт, в котором определяются операторы импорта (import statements), функции (functions), классы (classes) и переменные (variables). Файлы ZIP и DLL тоже могут быть модулями. Название модуля хранится в глобальной переменной (global variable) в виде строки (string).

В46. Какие в питоне есть модули для работы с файлами?

Питон предлагает следующие библиотеки и модули для обработки текстов и двоичных файлов:

os
os.path
shutil

В47. Можете коротко объяснить, как используются модули sqlite3, ctypes, pickle, traceback и itertools.

  • sqlite3 помогает обрабатывать базы данных, например SQLite
  • ctypes позволяет создавать в питоне типы данных из Си и обрабатывать их
  • pickle позволяет переносить любые структуры данных во внешние файлы
  • traceback позволяет извлекать, форматировать и выводить на печать трассы вызовов (stack traces)
  • itertools помогает работать с перестановками (permutations), комбинациями (combinations) и другими итерируемыми объектами (iterables).

В48. Расскажите про наследование (inheritance) в Python.

Когда один класс наследует из другого, его называют дочерним/производным/подклассом (child/derived/sub class), который наследует из родительского/базового/супер класса (parent/base/super class). Он наследует/получает все атрибуты и методы.

Наследование позволяет повторно использовать код и облегчает создание и дальнейшую работу приложений (applications). В Python поддерживаются следующие виды наследования:

  • Единичное наследование (Single Inheritance) — класс наследует из одного базового класса.
  • Множественное наследование (Multiple Inheritance) — класс наследует из двух или нескольких базовых классов.
  • Многоуровневое наследование (Multilevel Inheritance) — класс наследует из базового класса, который, в свою очередь, наследует из другого базового класса.
  • Иерархическое наследование (Hierarchical Inheritance) — два класса или несколько классов наследуют из одного базового класса (single base class).
  • Гибридное наследование (Hybrid Inheritance) — сочетание двух или нескольких видов наследования.

В49. Объясните, как в Python осуществляется управление памятью.

В Python объекты и структуры данных (data structures) находятся в закрытой динамически выделяемой области (private heap), которая управляется менеджером памяти Python. Он делегирует часть работы программам распределения ресурсов (allocators), закрепленным за конкретными объектами, и одновременно с этим следит, чтобы они не выходили за пределы динамически выделяемой области. По факту данной областью управляет интерпретатор (interpreter). Пользователь никак не контролирует данный процесс, даже когда манипулирует ссылками объектов на блоки памяти внутри динаической области. Менеджер памяти Python распределяет пространство динамической области среди объектов и другие внутренние буферы по требованию.

В50. Напишите программу на питоне, которая посчитает количество заглавных букв в файле.

>>> import os
>>> os.chdir('C:\\Users\\lifei\\Desktop')
>>> with open('Today.txt') as today:
....count=0
for i in today.read():
....if i.isupper():
........count+=1
....print(count)
26

В51. Как можно сделать скрипт Python, исполняемый в Unix?

Для этого должны выполняться два условия:

  • Файл скрипта должен быть в исполняемом режиме.
  • Первая строка должен начинаться с решетки (хэша, hash(#)), например: #!/usr/local/bin/python

В52. Какие функции или методы можно использовать для удаления файла в Python?

Для этого можно использовать remove() или unlink().

>>> import os
>>> os.chdir('C:\\Users\\lifei\\Desktop')
>>> os.remove('try.py')
>>>
>>> os.unlink('try.py')
>>>

Обе функции делают одно и то же, просто unlink — это традиционное название в Unix.

В53. Можете написать функцию для генерации такой пирамиды?

*
***
*****
*******
*********

def pyramid(n):
    for row in range(n):
        for space in range(n-row):
            print(' ',end='')
    for star in range(row):
        print('*',end='')
    for star in range(row+1):
        print('*',end='')
    print()

pyramid(5)

В54. Как можно вывести на печать содержимое файла?

>>> try:
....with open('tabs.txt','r') as f:
........print(f.read())
....except IOError:
........print("File not found")

В55. Расскажите про выражения лямбда (lambda expressions). Где они могут пригодиться?

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

>>> (lambda a,b:a if a>b else b)(3,3.5)
3.5

В данном примере входные данные обозначаются переменными a и b. То есть, если a > b, то возвращается a, в противном случае возвращается b. В качестве аргументов используются 3 и 3.5.

Лямбда позволяет обойтись без входных данных.

>>> (lambda :print("Hi"))()
Hi

В56. Что такое генератор (generator)?

В питоне генератор создает последовательность (sequence) значений, вдоль которой осуществляется итерация. То есть, это своего рода итерируемый объект (iterable). Мы пишем функцию, которая выдает (yield) значения по одному, а затем используем цикл for (for loop) для итерации вдоль нее.

def squares(n):
    i=1
    while(i<=n):
        yield i**2
    i+=1

>>> for i in squares(7):
....print(i)
1
4
9
16
25
36
49

В57. Ну тогда, что такое итератор (iterator)?

Итератор возвращает один объект за раз во время цикла итерации. Для создания итератора можно использовать функцию iter().

odds=iter([1,3,5,7,9])

Затем мы отправляем его в функцию next() каждый раз, когда хотим получить объект.

>>> next(odds)
1
>>> next(odds)
3
>>> next(odds)
5
>>> next(odds)
7
>>> next(odds)
9

Но при следующем вызове появится исключение остановки итерации (StopIteration exception), потому что закончили значения, по которым можно осуществлять итерацию.

>>> next(odds)
Traceback (most recent call last):
File “<pyshell#295>”, line 1, in
next(odds)
StopIteration

В58. Хорошо, мы спросили вас про генераторы (generators) и итераторы (iterators), и вы дали верные ответы. Но ведь они звучат очень похоже?

Так и есть, но между ними существуют тонкие различия:

  • Для генератора мы написали функцию, а для итератора можно использовать встроенные функции iter() и next().
  • Для генератора используется ключевое слово yield для выдачи по одному объекту за раз.
  • В генераторе может быть сколько угодно операторов yield.
  • Генератор сохраняет текущее состояние локальных переменных (local variables) каждый раз, когда yield приостанавливает цикл (loop). Итератор не использует локальные переменные, он работает только с итерируемым объектом (iterable).
  • Итератор можно использовать с помощью класса, а генератор — нет.
  • Генераторы работают быстро, компактно и проще.
  • Итераторы экономнее потребляют память.

В59. Что такое декоратор (decorator)?

Функция, которая расширяет другую функцию без внесения в нее изменений, оборачиваясь (wrap) вокруг нее. Посмотрим на примере.

>>> def decor(func):
....def wrap():
........print("$$$$$$$$$$$$$$$$$")
........func()
........print("$$$$$$$$$$$$$$$$$")

Декораторы — из сферы метапрограммирования (metaprogramming), в котором одна часть кода пытается изменить другую.

В60. Что такое временная подмена (Monkey Patching)?

Она модифицирует класс или модуль во время выполнения (at runtime), то есть представляет собой динамическую модификацию (dynamic modification). Пример:

from pkg.module import MyClass

def sayhello(self):
    print("Hello")

MyClass.sayhello=sayhello

Свободные вопросы, не связанные с техническими знаниями о Python.

Также следует быть готовым к вопросам на свободные темы, с помощью которых интевьюер хочет узнать получше ваш характер и ваши мотивации. Несколько примеров таких вопросов см. далее.

В61. Почему мы должны взять именно вас?

В62. В чем вы выросли как личность после ухода с последнего места?

В63. Расскажите про какую-нибудь ситуацию на прошлой работе, когда возникли сложности. Как вы их решили?

В64. Как бы вы урегулировали спор с коллегой? Возникали ли такие ситуации на прошлой работе?

В65. В чем бы вы хотели, чтобы мы были лучше по сравнению с прошлым работодателем?

В66. Удавалось ли вам когда-нибудь изменить чью-то позицию по рабочему вопросу?

В67. Что думаете про обмен слухами среди коллег?

В68. В чем ваше слабое место с точки зрения работы?

В69. Как вы думаете, в каком направлении будет развиваться отрасль в следующие 15 лет?

В70. Что вы оставили после себя на прошлой работе? Удалось ли вам развить инновационное решение?

В71. Как бы вы предпочли работать, самостоятельно или в небольшой/крупной группе?