Підготовка до співбесіди на позицію Python-розробника


Дізнайтесь більше про нові кар'єрні можливості в EchoUA. Цікаві проекти, ринкова оплата, гарний колектив. Надсилайте резюме та приєднуйтеся до нас.

На сьогодні число Python-програмістів продовжує рости, проте кількість робочих місць для них збільшується не так швидко. Сучасному розробникові треба бути конкурентоздатним, щоб пробитися на бажану позицію. Ми підготували статтю з темами і питаннями, яких працедавець може торкнутися на співбесіді, і доповнили їх невеликими поясненнями – по суті, це завдання по програмуванню на Python з рішеннями. Матеріал буде корисний тим, хто продовжує для повторення, а початківцям допоможе зорієнтуватися, куди робити перші кроки, на що звернути увагу. Сприймайте як своєрідний маяк.

Зміст статті :

  1. Робота зі списками.
  2. Відладка коду і тестування.
  3. Масиви.
  4. Декоратори функцій.
  5. Виключення.
  6. Ітератори.
  7. GIL.
  8. Передача аргументів.
  9. Питання поза певними категоріями.
  10. Додатково.
  11. Висновок.

Робота зі списками

Лямбда-вираз, генератори списків і вирази-генератори

Лямбда-вираз – скорочений метод створення однолінійних анонімних функцій. Їх простота часто (але не завжди) робить код стрункішим і читабельним, ніж класичне оголошення функцій. З іншого боку, та ж простота обмежує можливості і зони застосування лямбда-виразів.

Генератори списків забезпечують короткий синтаксис для створення списків. Вони використовуються для складання списків, в яких кожен елемент – результат деякої операції (операцій) з елементами іншої послідовності або ітератором. Генератори списків можуть використовуватися для створення підпослідовності тих елементів, члени яких задовольняють певній умові. Генератори списків в Python – своєрідна альтернатива вбудованим функціям map () і filter ().

Лямбда-вираз з функціями map () і filter () і генератори списків схожі, тому вибір одного з цих інструментів суб’єктивний і залежить від випадку. Але слід зазначити, що генератори списків виконуються дещо швидше – виклик лямбда-функції створює новий стековий кадр.

Вирази-генератори синтаксично і функціонально схожі на генератори списків, але є важливі відмінності між їх механізмами роботи і сферами застосування. Ітерація при накладенні вираз-генератора або генератора списку робитиме все те ж саме, але генератор списків спочатку створить цілий список в пам’яті, тоді як вираз-генератор створюватиме елементи на ходу в міру необхідності. Вираз-генератори можуть бути використані у великій або навіть нескінченній кількості послідовностей. А генерування значень на вимогу забезпечує підвищення продуктивності і зниження використання пам’яті. Проте слід зазначити, що стандартні методи списків list у Python можуть застосовуватися на результатах виконання генератора, але не на самому генераторі.

У чому різниця між списком і кортежем?

Основна різниця: список може змінюватися, а кортеж – ні. Робота з кортежами швидша, ніж зі списками. Якщо необхідно визначити постійний набір значень, і все, що з ним коли-небудь потрібно робити, – це перебирати його елементи, рациональнее використати кортеж замість списку. Кортеж також може виступати ключем для словників, на відміну від списку.

Самі просунуті кандидати скажуть, що кортежі неоднорідні і їх використання аналогічно використанню struct у мові програмування С. Списки ж аналогічні звичним масивам.

Відладка коду та тестування

Який підхід ви використовуєте для модульного тестування в Python?

Фундаментальна відповідь на це питання відноситься до використання фреймворка Python – unittest.

Unittest підтримує автоматизацію тестів, використання загального коду для налаштування і завершення тестів, об’єднання тестів в групи, а також дозволяє відділяти тести від фреймворка для формування звітів. Модуль unittest представляє класи, що спрощують підтримку цих якостей для набору тестів.

Вас можуть попросити описати ключові елементи структури unittest, а саме:

  • випробувальний стенд (test fixture);
  • тестовий випадок (test case);
  • набір тестів (test suite);
  • виконавець тестів (test runner).

Mock – недавнє доповнення до модуля unittest, яке дозволяє замінювати частину тестованої системи mock- об’єктами. Mock тепер частина стандартної бібліотеки Python, доступної як unittest.mock у Python 3.3 і новіше.

Як робиться відладка програми на Python? Чи можна виконати Python- код покроково?

У Python є вбудований модуль pdb, який визначає інтерактивний відладчик для початкового коду програм Python.

python - m pdb mypyscript.py

Запуск pdb з інтерпретатора:

>>> import pdb_script>>> import pdb>>> pdb.run ('pdb_script.MyPyObj (6).go()') > (1)()(Pdb)

Із скриптового файлу Python:

import pdbclass MyObj (object): count = 5 def __init__ (self): self.count= 9 def go (self): for i in range (self.count): pdb.set_trace () print i returnif __name__ == '__main__': MyObj (5).go () 

Масиви

Розгляньте два підходи нижче для ініціалізації масиву і масивів. У чому різниця між цими підходами, і чому вам слід використати тільки один з них?

# Ініціалізація масиву -- метод 1...x = [[1,2,3,4]] * 3x
[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]# Ініціалізація масиву -- метод 2...y = [[1,2,3,4] for_in range (3)]y[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]# Який метод слід використати і чому?

Здається, що обидва підходи забезпечують один і той же результат, але є важлива відмінність. Другий метод, як і очікувалося, створює масив з трьох елементів, кожен з яких є незалежним чотирьохелементним масивом. У першому ж методі усі члени масиву вказують на один і той же об’єкт. Це може привести до найбільш вірогідної непередбаченої і небажаної поведінки, як показано нижче.

# Зміна масиву X з попереднього фрагмента коду: x[0][3] = 99x[[1, 2, 3, 99], [1, 2, 3, 99], [1, 2, 3, 99]]# Сумнівно, що таке ви хотіли отримати саме такий результат...
# Зміна масиву Y з попереднього фрагмента коду: y[0][3] = 99y[[1, 2, 3, 99], [1, 2, 3, 4], [1, 2, 3, 4]]# А це вже те, що цілком слід було чекати....

Що таке NumPy? Чому краще використати масиви NumPy замість списків Python?

NumPy – пакет Python для наукових обчислень, який здатний працювати з великими об’ємами даних. Він включає потужний N-мірний об’єкт масиву і набір просунутих функцій.

Також є певні причини, згідно з якими краще використати масиви,NumPy:

  • масиви NumPy компактніші, ніж списки;
  • запис і читання виконуються швидше;
  • масиви ефективніші із-за збільшення функціональності списків.

Як можна створити масив NumPy в Python?

Існує два способи створення масивів NumPy:

Перший:

import numpynumpy.array ([])

Другий:

import numpynumpy.empty (shape= (0,0))

Декоратори функцій

Навіщо використовувати декоратори функцій?

Декоратори – об’єкти в Python, які викликаються і використовуються для зміни або розширення функції або класу. Привабливість декораторів в тому, що один декоратор може бути застосований до декількох функцій (чи класам). Наприклад, в Flask використовуються декоратори як механізм додавання нових портів у веб-додатки. Приклади деяких найбільш поширених застосувань декораторів включають додавання синхронізації, примусове введення типів, логірування, передумови і постумови для класу чи функції.

Що таке classmethod, @staticmethod, @property?

Декоратори @classmethod, @staticmethod і @property використовуються для функцій, визначених усередині класів. Вони поводяться так:

class MyClass (object): def __init__ (self): self._ some_property = "properties класні" self._ some_other_property = "Дуже класні" def normal_method (*args,**kwargs): print ("викликаний normal_method ({0},{1}) ".format (args, kwargs))  @classmethod def class_method (*args,**kwargs): print ("викликаний class_method ({0},{1}) ".format (args, kwargs))  @staticmethod def static_method (*args,**kwargs): print ("викликаний static_method ({0},{1}) ".format (args, kwargs))  @property def some_property (self,*args,**kwargs): print ("викликане some_property getter ({0},{1},{2}) ".format (self, args, kwargs)) return self._ some_property
@some_property.setter def some_property (self,*args,**kwargs): print ("викликане some_property setter ({0},{1},{2}) ".format (self, args, kwargs)) self._ some_property = args[0] @property def some_other_property (self,*args,**kwargs): print ("викликане some_other_property getter ({0},{1},{2}) ".format (self, args, kwargs)) return self._ some_other_propertyo = MyClass ()# Недекоровані методи працюють подібно до звичайних, вони приймають поточний екземпляр (self) як перший аргументo.normal_method # >o.normal_method () # normal_method ((<__main__.MyClass instance at 0x7fdd2537ea28>,),{}) o.normal_method (1,2, x=3, y=4)  # normal_method ((<__main__.MyClass instance at 0x7fdd2537ea28>, 1, 2),{'y': 4, 'x': 3}) # Методи класу завжди приймають клас як перший аргументo.class_method# >o.class_method ()# class_method ((,),{}) o.class_method (1,2, x=3, y=4)
# class_method ((, 1, 2),{'y': 4, 'x': 3}) # Статичні методи не мають аргументів окрім тих, які ви їм призначаєте при вызовеo.static_method# o.static_method ()# static_method ((),{}) o.static_method (1,2, x=3, y=4) # static_method ((1, 2),{'y': 4, 'x': 3}) # Декоратори properties реалізуються за допомогою геттерів і сетерів. Викликати їх відкрито - помилка# Атрибути "тільки для читання" можуть бути визначені геттером без сетера (як, наприклад some_other_property) o.some_property# Викликає some_property getter (<__main__.MyClass instance at 0x7fb2b70877e8>,(),{}) # 'properties чудові' o.some_property ()# Викликає some_property getter (<__main__.MyClass instance at 0x7fb2b70877e8>,(),{}) # Traceback (most recent call last) :# File "", line 1, in # TypeError: 'str' object is not callableo.some_other_property
# Викликає some_other_property getter (<__main__.MyClass instance at 0x7fb2b70877e8>,(),{}) # 'Пишність'# o.some_other_property ()# Викликає some_other_property getter (<__main__.MyClass instance at 0x7fb2b70877e8>,(),{}) # Traceback (most recent call last) :# File "", line 1, in # TypeError: 'str' object is not callableo.some_property = "Сама пишність"# Викликає some_property setter (<__main__.MyClass object at 0x7fb2b7077890>,(' сама пишність',),{}) o.some_property# Викликає some_property getter (<__main__.MyClass object at 0x7fb2b7077890>,(),{}) # 'Сама пишність' o.some_other_property = " прекрасно"# Traceback (most recent call last) :# File "", line 1, in # AttributeError: can't set attributeo.some_other_property# Викликає some_other_property getter (<__main__.MyClass object at 0x7fb2b7077890>,(),{}) # 'Прекрасно'

Створіть логіруємий декоратор

Можливо, вам буде потрібно логірувати те, що виконує певна функція. Як правило, логірування прописується усередині функції (класу). Проте, іноді треба відстежити поведінку самої функції усередині програми.

Подивіться на невеликий декоратор, який можна використати для запису назви будь-якої функції і того, що вона робить :

import loggingdef log (func): """ Логіруємо яка функція викликається """ def wrap_log (*args, **kwargs): name = func.__name__ logger = logging.getLogger (name) logger.setLevel (logging.INFO)  # Відкриваємо файл лігв для запису
fh = logging.FileHandler ("%s.log" % name) fmt = '% (asctime) s - % (name) s - % (levelname) s - % (message) s' formatter = logging.Formatter (fmt) fh.setFormatter (formatter) logger.addHandler (fh) logger.info ("Виклик функції: %s" % name) result = func (*args, **kwargs) logger.info ("Результат: %s" % result) return func return wrap [email protected] double_function (a): """ Множимо отриманий параметр """ return a*2if __name__ == "__main__": value = double_function (2)

Цей невеликий скрипт містить функцію log, яка приймає функцію як єдиний аргумент. Ми створюємо об’єкт логгер, а назва лог-файла така ж, як і у функції. Після цього функція log записуватиме, як наша функція була викликана і що вона повертає, якщо повертає.

Виключення

Що таке виключення?

Виняткові ситуації, або виключення (exceptions) – це помилки, виявлені при виконанні коду. Наприклад, до чого приведе спроба читання неіснуючого файлу? А раптом файл був випадково видалений, поки програма працювала? Такі ситуації обробляються за допомогою виключень.

Якщо ж Python не може зрозуміти, як обійти ситуацію, що склалася, то йому не залишається нічого, окрім як повідомити про знайдену помилку.

Простий приклад виключення – ділення на нуль:

100 / 0Traceback (most recent call last): File "", line 1, in>>> 100 / 0ZeroDivisionError: division by zero

В цьому випадку інтерпретатор повідомив про виключення ZeroDivisionError – ділення на нуль.

Ієрархія виключень. Які системні виключення вам знайомі?

Виключення, яке ви не бачите при виконанні коду, – BaseException – це базове виключення, яке наслідують інші.

У ієрархії виключень виділяють дві основні групи:

  1. Системні виключення і помилки.
  2. Призначені для користувача виключення.

До системних відносяться:

  • SystemExit – виключення, що породжується функцією sys.exit при виході з програми;
  • KeyboardInterrupt – виникає при перериванні програми користувачем (зазвичай поєднанням клавіш Ctrl + C).
  • GeneratorExit – виникає при виклику методу close об’єкту generator.

Більше про роботу виключень ви можете дізнатися в офіційному керівництві.

Ітератори

Що таке ітератор?

У чому різниця між ітератором і генератором?

Ці терміни тісно пов’язані (будь-який генератор – це ітератор), їх досить часто плутають, що іноді призводить до непорозуміння. Ітератор – загальніша концепція. Це об’єкт, у якого визначені два методи: __next__ і __iter__. З іншого боку, генератор – це ітератор. Але не навпаки. Генератор може виходити використанням ключового слова yield у тілі функції.

def squares (start, stop) :
for i in range (start, stop): yield i * igenerator = squares (a, b)

GIL

Концепція GIL полягає в тому, що в кожен момент часу тільки один потік може виконуватися процесором. Це зроблено для того, щоб між потоками не було боротьби за окремі змінні. Виконуваний потік дістає доступ до усього оточення. Така особливість реалізації потоків в Python значно спрощує роботу з потоками і дає певну потокобезопасность (thread safety).

Передача аргументів

Як передаються незмінні об’єкти?

Незмінні об’єкти передаються “за значенням”. Такі об’єкти, як цілі числа і рядки, передаються у вигляді посилань на об’єкти, а не у вигляді копій об’єктів.

Як передаються змінювані об’єкти?

Змінювані об’єкти передаються “за вказівником”. Такі об’єкти, як списки і словники, також передаються у вигляді посилань на об’єкти, що дуже схоже на те, як в мові C передаються вказівники на масиви – змінювані об’єкти допускають можливість безпосередньої зміни усередині функції так само, як і масиви в мові C.

Приклад:

>>> def f (a): # Імені a привласнюється переданий об'єкт ... a = 99 # Змінюється тільки локальна змінна...>>> b = 88>>> f (b) # Спочатку імена a і b посилаються на одно і те ж число 88>>> print (b)  # Змінна b не изменилась88

У цьому фрагменті у момент виклику функції f (b) змінній a привласнюється об’єкт 88, але змінна a існує тільки усередині викликаної функції. Зміна змінної a усередині функції не робить впливу на оточення, звідки була викликана функція, – просто у момент виклику створюється абсолютно новий об’єкт a.

Що буде виведене після другого виклику append () в коді нижче?

>>> def append (list=[]) :... # додавання довжини списку у список... list.append (len (list))... return list...>>> append (['a','b'])['a', 'b', 2]>>>>>> append () # виклик без аргументу використовує значення list за умовчанням [][0]>>>>>> append () # Але що станеться при повторному виклику append без аргументу?

Коли значенням за умовчанням для аргументу функції є вираження, воно обчислюється тільки один раз, а не завжди при виклику функції. Таким чином, після того, як аргумент list ініціалізував в порожній масив, подальші виклики функції без аргументів продовжать використати той же самий масив, що ініціалізував спочатку.

>>> append () # при першому виклику без аргументу використовується значення за умовчанням [][0]>>> append () # але потім...[0, 1]>>> append () # послідовні виклики розширюють список за умовчанням[0, 1, 2]>>> append () # і так триває...[0, 1, 2, 3]

Як можна змінити застосування методу append в попередньому питанні, щоб уникнути небажаної поведінки, описаної там?

Є альтернативна реалізація методу append, яка розв’яже проблему:

>>> def append (list=None) :... if list is None: list =[] # Збільшує довжину списку... list.append (len (list)) return list...>>> append ()[0]>>> append ()[0]

Питання поза певними категоріями

Як можна поміняти місцями значення двох змінних усередині рядка в Python?

Розглянемо простий приклад:

>>> x = 'X'>>> y = 'Y'

У багатьох інших мовах програмування при заміні значень X і Y вимагається виконати щось подібне до цього:

>>> tmp = x>>> x = y>>> y = tmp>>> x, y ('Y', 'X')

Але в Python існує можливість зробити це за допомогою одного рядка коду таким чином :

>>> x, y = y, x>>> x, y ('Y', 'X')

Що буде виведене останнім оператором нижче?

>>> flist =[]>>> for i in range (3) :... flist.append (lambda: i)...>>> [f () for f in flist] # що буде виведене?

У будь-якому замиканні в Python змінні зв’язуються по імені. Таким чином, в приведеному вище рядку кода буде виведене наступне:

[2, 2, 2]

Але, ймовірно, це не зовсім те, що автор коду вище хотів би бачити.
Вихід з ситуації – створення окремої функції або передача аргументів по їх імені:

>>> flist =[]>>> for i in range (3) :... flist.append (lambda i = i: i)...>>> [f () for f in flist][0, 1, 2]

Для чого служить ключове слово ” self”?

Ключове слово self – змінна, яка відноситься до екземпляру об’єкту. Коли створюється клас, явне посилання на об’єкт того ж типу класу відсутнє. Тому, щоб посилатися на поточний клас або об’єкт, в Python використовується ключове слово self.

class User: def __init__ (self): self.name = 'Ivan Ivanov' self.age = 16 user_obj = User()
user_obj.name # self.name містить 'Ivan Ivanov' в якості значення

Для чого служить ключове слово “yield”?

Ключове слово yield може обернути будь-яку функцію в генератор. Воно працює подібно до оператора return, з тією різницею, що ключове слово повертатиме об’єкт-генератор. Також функція може здійснювати декілька викликів ключового слова yield.

def testgen (index): weekdays =['sun','mon','tue','wed','thu','fri','sat'] yield weekdays[index] yield weekdays[index+1]day = testgen (0) print next (day), next (day) #output: sun mon

Що таке init.py? Як імпортувати клас з іншого каталогу?

__init__.py в основному використовується для ініціалізації пакетів Python.

Файл __init__.py у каталозі lstm_m вказує інтерпретатору Python, що цей каталог повинен оброблятися як пакет Python.

Як імпортувати клас з іншого каталогу?

Зазвичай __init__.py є порожнім файлом. А якщо нам треба використати lstm.py у файлі run.py, то його треба імпортувати таким чином:

from lstm_m import lstm

Крім того, усередині теки модуля має бути файл __init__.py, призначений для імпорту.

Які вбудовані типи існують в Python?

Існують змінювані і незмінні вбудовані типи Python.

Змінювані:

  • списки;
  • множини;
  • словники.

Незмінні:

  • рядки;
  • кортежі;
  • числа.

Слід пам’ятати, що вище перераховані тільки основні типи. Насправді їх більше шести.

Що таке docstring в Python?

Рядок документації в Python (docstring) – спосіб документування функцій, модулів і класів. Стандарти оформлення – на офіційному сайті.

Як можна конвертувати число в рядок?

Для перетворення числа в рядок, як правило, використовують вбудовану функцію str(), хоча є і інші способи, такі як "{0: d}".format (число) і "%d"%число. Якщо ви хочете перетворити десяткове число у вісімкове (oct – octal) або шістнадцятиричне (hex – hexadecimal), використайте вбудовану функцію oct() чи hex() відповідно.

У чому різниця між Xrange і range?

Функція xrange() повертає об’єкт xrange, тоді як range() повертає список і використовує ту ж кількість пам’яті, незалежно від розміру функції.

Як побачити методи або атрибути об’єкту?

Команда dir(x) повертає відсортований список імен атрибутів для будь-якого переданого в неї об’єкту. Якщо жоден об’єкт не вказаний, dir () повертає імена в поточній зоні видимості.

Додатково

Якщо ви володієте англійською мовою, то рекомендуємо пройти онлайн-тести і перевірити свої знання перед співбесідою.

Підбірки матеріалів для вивчення Python від нас: сайти і книги, ще книги, ще сайти. Короткі відповіді на популярні питання, а також декілька хитрощів. Не забудьте вирішити завдання: ось список ресурсів, де це можна зробити.

Висновок

Питання і ради, представлені в цій статті, можуть бути дуже цінними допоміжними засобами для підготовки претендентів до співбесід. Ми сподіваємося, розробники знайдуть їх корисними для самостійного тестування своїх знань перед зустріччю з працедавцем. Але не слід забувати, що усі представлені вище питання – один з декількох інструментів відбору кандидатів на посаду у складі певної стратегії. Готуйтеся уважно і ретельно. Удачі!

При підготовці використовувалися матеріали: The Vital Guide to Python Interviewing, Must Have Python Interview Questions, 15 Essential Python Interview Questions, Python Interview Questions and Answers

Київ, Харків, Одеса, Дніпро, Запоріжжя, Кривий Ріг, Вінниця, Херсон, Черкаси, Житомир, Хмельницький, Чернівці, Рівне, Івано-Франківськ, Кременчук, Тернопіль, Луцьк, Ужгород, Кам'янець-Подільський, Стрий - за статистикою саме з цих міст програмісти найбільше переїжджають працювати до Львова. А Ви розглядаєте relocate?


Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *