24940
Вопросы с собеседований по Python @workakkk - админ @machinelearning_interview - вопросы с собесдований по Ml @pro_python_code - Python @data_analysis_ml - анализ данных на Python @itchannels_telegram - 🔥 главное в ит РКН: clck.ru/3FmrFd
🖥 Что такое перегрузка операторов? Есть ли перегрузка операторов в Python
Перегрузка операторов в Python – это возможность с помощью специальных методов в классах переопределять различные операторы языка. Имена таких методов включают двойное подчеркивание спереди и сзади.
Под операторами в данном контексте понимаются не только знаки +, -, *, /, обеспечивающие операции сложения, вычитания и др., но также специфика синтаксиса языка, обеспечивающая операции создания объекта, вызова объекта как функции, обращение к элементу объекта по индексу, вывод объекта и другое
На самом деле перегрузка операторов в пользовательских классах используется не так часто, если не считать конструктора. Но сам факт наличия такой особенности объектно-ориентированного программирования, требует отдельного рассмотрения темы.
Возможность перегрузки операторов обеспечивает схожесть пользовательского класса со встроенными классами Python. Ведь все встроенные типы данных Питона – это классы. В результате все объекты могут иметь одинаковые интерфейсы. Так если ваш класс предполагает обращение к элементу объекта по индексу, например a[0], то это можно обеспечить.
Пусть будет класс-агрегат B, содержащий в списке объекты класса A:class A:
def __init__(self, arg):
self.arg = arg
def __str__(self):
return str(self.arg)
class B:
def __init__(self, *args):
self.aList = []
for i in args:
self.aList.append(A(i))
group = B(5, 10, 'abc')
Чтобы получить элемент списка, несомненно, мы можем обратиться по индексу к полю aList:print(group.aList[1])
Однако куда интереснее извлекать элемент по индексу из самого объекта, а не из его поля:class B:
def __init__(self, *args):
self.aList = []
for i in args:
self.aList.append(A(i))
def __getitem__(self, i):
return self.aList[i]
group = B(5, 10, 'abc')
print(group.aList[1]) # выведет 10
print(group[0]) # 5
print(group[2]) # abc
Это делает объекты класса B похожими на объекты встроенных в Python классов-последовательностей (списков, строк, кортежей). Здесь метод __getitem__() перегружает операцию извлечения элемента по индексу. Другими словами, этот метод вызывается, когда к объекту применяется операция извлечения элемента: объект[индекс].
Бывает необходимо, чтобы объект вел себя как функция. Это значит, если у нас есть объект a, то мы можем обращаться к нему в нотации функции, т. е. ставить после него круглые скобки и даже передавать в них аргументы:a = A()
a()
a(3, 4)
Метод __call__() автоматически вызывается, когда к объекту обращаются как к функции. Например, здесь во второй строке произойдет вызов метода __call__() некогоКласса:
объект = некийКласс()
объект([возможные аргументы])
Пример:class Changeable:
def __init__(self, color):
self.color = color
def __call__(self, newcolor):
self.color = newcolor
def __str__(self):
return "%s" % self.color
canvas = Changeable("green")
frame = Changeable("blue")
canvas("red")
frame("yellow")
print(canvas, frame)
В этом примере с помощью конструктора класса при создании объектов устанавливается их цвет. Если требуется его поменять, то достаточно обратиться к объекту как к функции и в качестве аргумента передать новый цвет. Такой обращение автоматически вызовет метод __call__(), который, в данном случае, изменит атрибут color объекта.
@python_job_interview
🖥 Задача с собеседования | Уровень: #Junior
Условие:
Найти «счастливый» ip. Он считается счастливым, если сумма двух байтов с левой стороны равняются сумме двух байтов с правой стороны.
На вход функции всегда идёт строка с ipv4 адресом.
Пример:is_happy_ip("255.255.255.255") -> True
is_happy_ip("0.0.0.1") -> False
is_happy_ip("101.78.170.9") -> True
P.S. 1 байт - число от 0 до 255
Делитесь своим решением в комментариях👇def is_happy_ip(ip: str) -> bool:
g = ip.split('.')
return int(g[0])+int(g[1]) == int(g[2])+int(g[3])
@python_job_interview
🖥 Тестовое задание для стажера в юнит Market Intelligence
Нужно реализовать HTTP сервис для голосования. Например, для выбора самого популярного покемона. UI не нужен, достаточно сделать JSON API сервис. Должна быть возможность:
Задача
Нужно реализовать HTTP сервис для голосования. Например, для выбора самого популярного покемона. UI не нужен, достаточно сделать JSON API сервис. Должна быть возможность:
▪ Создать новое голосование с разными вариантами ответов
▪ Отдать свой голос за какой-либо вариант
▪ Получить текущий результат голосования
Реализовать методы:
- POST /api/createPoll/ создать голосование c вариантами ответов
- POST /api/poll/ проголосовать за конкретный вариант: <poll_id, choice_id>
- POST /api/getResult/ получить результат по конкретному голосованию: <poll_id>
Структура и формат входных и выходных данных на ваше усмотрение.
Описание идеального решения
▪ Задание декомпозировано, составлен иерархический список работ. Каждый пункт из этого списка может быть реализован за небольшое время.
▪ Составлена схема архитектуры со всеми сущностями и их связями в Miro
▪ Код слабо связан, функции не имеют побочных эффектов
▪ История коммитов осмысленная. По ней видно, в каком порядке решалась задача.
▪ Покрытие тестами >70%
Требования
▪Язык: Python
▪Результаты голосования должны храниться в базе данных. Мы обычно используем PostgreSQL и MongoDB, но можно выбрать любую другую.
▪Код нужно выложить на github (просьба не делать форк этого репозитория, чтобы не плодить плагиат)
▪Предоставить инструкцию по запуску приложения. В идеале (но не обязательно) – использовать контейнеризацию с возможностью запустить проект командой docker-compose up
▪Сервис должен отвечать на 8000 порту
Усложнения
▪ Написаны тесты (постарайтесь достичь покрытия в 70% и больше)
▪ Опишите, как изменится архитектура, если мы ожидаем большую нагрузку (Реализация не требуется)
▪ Опишите, как можно защититься от накруток (Реализация не требуется)
▪ Попробуйте оценить, какую нагрузку в RPS сможет выдержать ваш сервис
Что будет оцениваться
▪ Декомпозиция задачи (например, в виде вложенного списка подзадач)
▪ Архитектура решения
▪ Корректность реализации
▪ Подход к тестированию
▪ Понятность инструкций и документации
▪ Codestyle
▪ [Python] Typehints
@python_job_interview
📚 20 лучших бесплатных книг по Python для начинающих и продвинутых программистов
Настоящий новогодний подарок всем Python разработчикам.
✔️ Смотреть список
@python_job_interview
🖥 Определение магического квадрата
Магические квадраты издавна интриговали воображение людей: дата изготовления древнейшей сохранившейся таблицы относится к 2200 г. до н.э. Магический квадрат – это квадратная таблица размера n х n, составленная из всех чисел 1, 2, 3 … n2 таким образом, что суммы по каждому столбцу, каждой строке и каждой диагонали равны между собой. Напишем программу, которая определяет, можно ли считать матрицу магическим квадратом.
Входные данные:
Число n, затем n строк с n цифр в каждой.
#Пример ввода
3
8 1 6
3 5 7
4 9 2
Выходные данные:
YES, если введенная матрица является магическим квадратом, и NO в обратном случае.
#Пример вывода
YES
Решение
Способ 1:n = int(input())
matrix = [list(map(int, input().split())) for _ in range(n)]
if all(i in sum(matrix,[]) for i in range(1, n**2 + 1)):
print('YES' if all(sum(i) == sum(j) == sum([matrix[i][i] for i in range(n)]) == sum([matrix[n-i-1][i] for i in range(n)]) for i in matrix for j in list(map(list, zip(*matrix)))) else 'NO')
else:
print('NO')
Способ 2 – с магической константой и множествами:
n = int(input())
square = [[*map(int, input().split())] for _ in range(n)]
m_const = n * (1 + n ** 2) // 2
print(('NO', 'YES')[all(sum(el) == m_const for x in (((square[i][i] for i in range(n)),(square[i][~i] for i in range(n))), square, zip(*square)) for el in x) and set(sum(square, [])) == set(range(1, n ** 2 + 1))])
@python_job_interview
🌀 20 практических заданий с рекурсией в Python. Python практика.
В этой статье мы предложим практические задания для самостоятельного решения на Python. Практика написания кода – лучший способ, прокачать свои навыки.
10 Простых заданий + 10 сложных заданий.
✔️ Смотреть
@python_job_interview
🖥♟ Задача ходы шахматного ферзя
На шахматной доске 8 х 8 стоит ферзь. Отметьте положение ферзя на доске и все клетки, которые бьет ферзь. Клетку, где стоит ферзь, отметьте буквой Q, клетки, которые бьет ферзь, отметьте звездочками *, остальные клетки заполните точками. Шахматный ферзь может ходить по вертикали, горизонтали и по диагоналям.
Входные данные:
Координаты ферзя на шахматной доске в формате номер столбца (буква от a до h, слева направо) и номер строки (цифра от 1 до 8, снизу вверх).
Пример ввода:
c4
Выходные данные:
Программа выводит стилизованное изображение шахматной доски со схемой возможных передвижений ферзя.
Пример вывода:
. . * . . . * .
. . * . . * . .
* . * . * . . .
. * * * . . . .
* * Q * * * * *
. * * * . . . .
* . * . * . . .
. . * . . * . .
Решение
Способ 1:
x, y, board = *('abcdefgh87654321'.index(i) % 8 for i in input()), range(8)
[print(*['?Q**.'[len({j - x, x - j, i - y, y - i})] for j in board]) for i in board]
Способ 2:
x, y = ('abcdefgh87654321'.index(i) % 8 for i in input())
directions = lambda i, j: (j - i == x - y) + (j + i == x + y) + ((j == x) != (i == y))
[print(*['.*Q'[directions(i, j)] for j in range(8)]) for i in range(8)]
Способ 3:coor = input()
board = [['.'] * 8 for _ in range(8)]
y, x = 8 - int(coor[1]), ord(coor[0]) - 97
for i in range(8):
for j in range(8):
if (y == i) or (x == j) or abs(y - i) == abs(x - j):
board[i][j] = '*'
board[y][x] = 'Q'
for line in board:
print(*line)
✔️ Пишите свои решения в комментариях👇
@python_job_interview
🖥 Задача с собеседования Junior
Условие:
Ваша задача — написать функцию, которая проверит, все ли значения увеличиваются на один
Пример:[-1, 0, 1, 2, 3] -> True
[-1, 0, 1, 3, 4]) -> False
[0, 1] -> True
[1, 0] -> False
✔️ Пишите свои решения в комментариях👇
#задача_с_собеседования
@python_job_interview
🖥 Что такое утиная типизация
Неявная типизация, латентная типизация или утиная типизация (англ. Duck typing) – вид динамической типизации, применяемой в некоторых языках программирования (Perl, Smalltalk, Python, Objective-C, Ruby, JavaScript, Groovy, ColdFusion, Boo, Lua, Go, C#), когда границы использования объекта определяются его текущим набором методов и свойств, в противоположность наследованию от определённого класса. То есть считается, что объект реализует интерфейс, если он содержит все методы этого интерфейса, независимо от связей в иерархии наследования и принадлежности к какому-либо конкретному классу.
Утиная типизация решает такие проблемы иерархической типизации, как:
- невозможность явно указать (путём наследования) на совместимость интерфейса со всеми настоящими и будущими интерфейсами, с которыми он идейно совместим;
-экспоненциальное увеличение числа связей в иерархии типов при хотя бы частичной попытке это сделать.
@python_job_interview
🖥 Вопросы с собеседований python. Как проходит собеседование Python-разработчика: вопросы для джуниоров и мидлов.
Привет! Меня зовут Руслан. Около 12 лет я занимаюсь разработкой, из них девять — на Python. За это время я собеседовался на разные позиции десятки раз и сам провёл примерно пару сотен собеседований. Не всегда успешно :/ В этой статье поговорим о том, как снизить вероятность провалов и к чему быть готовым.
➡️ Читать дальше
@python_job_interview
🖥 Реальное пракическое тестовое задание в команду Assisted team (Python)
Прикрепляем два XML (1 файл, 2 файл) – это ответы на поисковые запросы, сделанные к одному из наших партнёров. В ответах лежат варианты перелётов (тег Flights) со всей необходимой информацией, чтобы отобразить билет на Aviasales.
На основе этих данных, нужно сделать вебсервис, в котором есть эндпоинты, отвечающие на следующие запросы:
- Какие варианты перелёта из DXB в BKK мы получили?
- Самый дорогой/дешёвый, быстрый/долгий и оптимальный варианты
- В чём отличия между результатами двух запросов (изменение маршрутов/условий)?
Язык реализации: python3 Формат ответа: json Используемые библиотеки и инструменты — всё на твой выбор.
Оценивать будем умение выполнять задачу имея неполные данные о ней, умение самостоятельно принимать решения и качество кода.
@python_job_interview
🖥 Что такое __new__. И чем он отличается от __init__. В какой последовательности они выполняются ?
Основное различие между этими двумя методами состоит в том, что __new__ обрабатывает создание объекта, а __init__ обрабатывает его инициализацию.__new__ вызывается автоматически при вызове имени класса (при создании экземпляра), тогда как __init__ вызывается каждый раз, когда экземпляр класса возвращается __new__, передавая возвращаемый экземпляр в __init__ в качестве параметра self, поэтому даже если вы сохранили экземпляр где-нибудь глобально/статически и возвращали его каждый раз из __new__, для него все-равно будет каждый раз вызываться __init__.
Из вышесказанного вытекает что сначала вызывается __new__, а потом __init__
@python_job_interview
🖥 Какие методы есть у генераторов ?
🟠__next__() – начинает или продолжает исполнение функции-генератора. Результат текущего yield-выражения будет равен None. Выполнение затем продолжается до следующего yield-выражения, которое передаёт значение туда, где был вызван next. Если генератор завершается без возврата значения при помощи yield, возникает исключение StopIteration. Метод обычно вызывается неявно, то есть циклом for или встроенной функцией next().
🟠send(value) – продолжает выполнение и отправляет значение в функцию-генератор. Аргумент value становится значением текущего yield-выражения. Метод send() возвращает следующее значение, возвращённое генератором, или выбрасывает исключение StopIteration, если генератор завершается без возврата значения. Если send() используется для запуска генератора, то единственным допустимым значением является None, так как ещё не было выполнено ни одно yield-выражение, которому можно присвоить это значение.
🟠throw(type[, value[, traceback]]) – выбрасывает исключение типа type в месте, где был приостановлен генератор, и возвращает следующее значение генератора (или выбрасывает StopIteration). Если генератор не обрабатывает данное исключение (или выбрасывает другое исключение), то оно выбрасывается в месте вызова.
🟠close() – выбрасывает исключение GeneratorExit в месте, где был приостановлен генератор. Если генератор выбрасывает StopIteration (путём нормального завершения или по причине того, что он уже закрыт) или GeneratorExit (путём отсутствия обработки данного исключения), close просто возвращается к месту вызова. Если же генератор возвращает очередное значение, выбрасывается исключение RuntimeError. Метод close() ничего не делает, если генератор уже завершён.
Можно ли извлечь элемент генератора по индексу
Нет, будет ошибка. Генератор не поддерживает метод getitem.
@python_job_interview
🖥 Что такое генератор
В зависимости от контекста, может означать либо функцию-генератор, либо итератор генератора (чаще всего, последнее). Методы __iter__ и __next__ у генераторов создаются автоматически.
С точки зрения реализации, генератор в Python — это языковая конструкция, которую можно реализовать двумя способами: как функция с ключевым словом yield или как генераторное выражение. В результате вызова функции или вычисления выражения, получаем объект-генератор типа types.GeneratorType. Канонический пример - генератор, порождающий последовательность чисел Фибоначчи, которая, будучи бесконечна, не смогла бы поместиться ни в одну коллекцию. Иногда термин применяется для самой генераторной функции, а не только объекта, возвращенного ей в качестве результата.
Так как в объекте-генераторе определены методы next и iter, то есть реализован протокол итератора, с этой точки зрения, в Python любой генератор является итератором.
Когда выполнение функции-генераторы завершается (при помощи ключевого слова return или достижения конца функции), возникает исключение StopIteration.
Что такое генераторная функция
Генераторная функция - функция, в теле которой встречается ключевое слово yield. Будучи вызвана, такая функция возвращает объект-генератор (generator object) (итератор генератора (generator iterator)).
Что делает yield
yield замораживает состояние функции-генератора и возвращает текущее значение. После следующего вызова __next__() функция-генератор продолжает своё выполнение с того места, где она была приостановлена.
В чем отличие [x for x in y] от (x for x in y)
Первое выражение возвращает список (списковое включение), второе – генератор.
@python_job_interview
🖥 Расскажите про замыкания. Какая проблема возникает при создание нескольких функций внутри одного контекста?
Когда вы определяете функцию внутри другой функции и используете локальные переменные внешней функции во вложенной, вы создаете замыкание. Время жизни этих переменных "продляется" в особой области видимости enclosing даже после завершения работы внешней функции. Пример: make_adder возвращает функцию-прибавлятор. Объект из переменной a будет жить и работать даже после выхода из make_adder:def make_adder(a):
def adder(x):
return a + x
return adder
plus_5 = make_adder(5)
print(plus_5(3)) # 8
Здесь я хочу коснуться одной популярной проблемы. Дело в том, что если мы создадим несколько функций внутри одного контекста, то они будут разделять одну область видимости enclosing. Рассмотрим пример создания трех функций в цикле:def make_adders():
adders = []
for a in range(3):
def adder(x):
return a + x
adders.append(adder)
return adders
adders = make_adders()
for adder in adders:
print(adder(2)) # 4 4 4
Вместо функций прибавляющих разные числа от 0 до 2, мы получили 3 одинаковых функции, потому что внутри себя они поддерживают ссылку на одну и ту же переменную a, значение которой останется равным 2 после выполнения всего цикла целиком.
Есть простой прием, помогающий "зафиксировать" значения переменной в моменте: достаточно добавить во вложенную функцию дополнительный аргумент со значением по умолчанию, равным нужной переменной a=a:def make_adders():
adders = []
for a in range(3):
def adder(x, a=a): # FIX!
return a + x
adders.append(adder)
return adders
adders = make_adders()
for adder in adders:
print(adder(2)) # 2 3 4
Еще лучше переименовать аргумент, чтобы избежать конфликтов имен и замечаний IDE, например, так:def adder(x, that_a=a): # FIX!
return that_a + x
@python_job_interview
🖥 Как производится отладка программы на Python
Python есть встроенный отладчик под названием pdb. Это простая консольная утилита, которая обладает основной функциональностью для отладки кода. Но если вы ищете что-то более продвинутое, то стоит обратить внимание на ipdb – отладчик с функциональностью из IPython.
Проще всего вызвать отладчик pdb из кода, где вы работаете:import pdb; pdb.set_trace()
Как только интерпретатор доберётся до этой строчки, запустится отладчик и в консоли будут доступны новые команды.
list()
Эта команда покажет часть кода, на выполнении которой сейчас находится интерпретатор. Можно передать два аргумента first и last для просмотра определённого участка кода. Если указать только first, то будет выведен код вокруг искомой строки.
up(p) и down(d)
Эти команды используются для передвижения по стеку вызовов. С их помощью можно отследить, откуда была вызвана текущая функция.
step() и next()
Другая пара не менее важных команд. С их помощью можно выполнять код построчно. Единственное различие между ними в том, что next() перейдёт к следующей строке вне зависимости от вызываемых функций, а step() перейдёт в вызванную функцию, если это возможно.
break()
Эта команда позволяет создавать брейкпоинты без внесений изменений в код. Ниже разберём этот этап более детально.
Краткий список команд отладчика pdb:
args() — выводит аргументы функции;
continue() или (cont) — продолжит выполнение до первого брейкпоинта или до завершения программы;
help() — выводит список доступных команд или подсказки по определённой команде;
jump() — перепрыгивает к выполнению указанной строчки кода;
list() — выводит исходный код программы вокруг выбранной строки;
expression() — выводит значение выражения;
pp — выводит значение в «красивом» виде;
quit или exit() — отменяет выполнение программы;
return() — завершает выполнение текущей функции.
@python_job_interview
🖥 Вопросы на собеседовании Python, ответы, на которые вам стоит знать.
В этой статье я превратил некоторые из своих заметок в 20 вопросов для собеседований, которые охватывают структуры данных, основные концепции программирования и лучшие практики Python.
Интересно, что многие из этих вопросов также задаются на собеседованиях по Data Science.
➡️ Читать дальше
@data_analysis_ml
🖥 Задача об определении латинского квадрата
Латинский квадрат – это квадратная матрица размером n x n, каждая строка и каждый столбец которой содержат все числа от 1 до n. В Средние века латинским квадратам приписывались магические свойства: такие таблицы использовались во время проведения оккультных ритуалов. Напишите программу для определения латинского квадрата.
Входные данные:
число n – количество строк и столбцов в матрице;
n строк, c n чисел в каждой.
Выходные данные:
Программа должна вывести YES, если введенная матрица является латинским квадратом, и NO в обратном случае.# Пример ввода:
4
2 3 4 1
3 4 1 2
4 1 2 3
1 2 3 4
Пример вывода:
#YES
Решение
Первый способ– с использованием функции sorted():
n = int(input())
matrix = [[int(i) for i in input().split()] for _ in range(n)]
for i in range(n):
if sorted(matrix[i]) != list(range(1, n + 1)) or sorted([matrix[j][i] for j in range(n)]) != list(range(1, n + 1)):
print('NO')
break
else:
print('YES')
Второй способ – с использованием функции all():
n = int(input())
square = [[*map(int,input().split()) ] for i in range(n)]
a = all([all([c in row for c in range(1,n + 1)]) for row in square])
b = all([all([c in row1 for c in range(1, n + 1)]) for row1 in zip(*square)])
print(('NO','YES')[a and b])
Третий способ – с использованием транспонирования и множеств:
n = int(input())
sq_set = set(range(1, n + 1))
square = []
for i in range(n):
square.append(list(map(int, input().split())))
tr_square = list(map(list, zip(*square)))
vector_1 = [set(row) == sq_set for row in square]
vector_2 = [set(row) == sq_set for row in tr_square]
vector = vector_1 + vector_2
if all(vector):
print('YES')
else:
print('NO')
@python_job_interview
🌀 Заполнение матрицы по спирали
Эта классическая задача часто встречается на собеседованиях и олимпиадах. Рассмотрим несколько способов решения на Python.
На вход программе подаются два натуральных числа n и m. Напишите программу, которая создает матрицу размером n х m, заполнив ее по спирали числами от 1 до n x m. Спираль начинается в левом верхнем углу и закручивается по часовой стрелке.
Пример ввода:7 6
Пример вывода:
1 2 3 4 5 6
22 23 24 25 26 7
21 36 37 38 27 8
20 35 42 39 28 9
19 34 41 40 29 10
18 33 32 31 30 11
17 16 15 14 13 12
Решение
Способ 1:
n, m = map(int, input().split())
matrix = [[0] * m for _ in range(n)]
dx, dy, x, y = 0, 1, 0, 0
for i in range(1, n * m + 1):
matrix[x][y] = i
if matrix[(x + dx) % n][(y + dy) % m]:
dx, dy = dy, -dx
x += dx
y += dy
for line in matrix:
print(*(f'{i:<3}' for i in line), sep='')
Способ 2:
n, m = (int(i) for i in input().split())
spiral = []
x, y, dx, dy, k = 0, 0, 1, 0, 1
spiral = [[0]* n for _ in range(m)]
for i in range(1, n * m + 1):
spiral[x][y] = i
nx, ny = x + dx, y + dy
if 0 <= nx < m and 0 <= ny < n and spiral[nx][ny] == 0:
x, y = nx, ny
else:
dx, dy = -dy, dx
x, y = x + dx, y + dy
for i in range(n):
for j in range(m):
print(str(spiral[j][i]).ljust(3), end=' ')
print()
Способ 3:
n, m = [int(i) for i in input().split()]
spiral = [[0] * m for _ in range(n)]
c = 1
for k in range(min(n // 2 + 1, m //2 + 1)):
for j in range(k, m - k):
if spiral[k][j] == 0:
spiral[k][j] = c
c += 1
for i in range(1 + k, n - k):
if spiral[i][m - k - 1] == 0:
spiral[i][m - k - 1] = c
c += 1
for j in range(m - k - 2, k - 1, -1):
if spiral[n - k - 1][j] == 0:
spiral[n - k - 1][j] = c
@python_job_interview
✔️Задача
Вам будет дано число, и вам нужно будет вернуть его в виде строки в расширенной форме.
Примеры
expanded_form(12) # Должно вернуть '10 + 2' expanded_form(42) #Должно вернуть '40 + 2' expanded_form(70304) # Должно вернуть '70000 + 300 + 4'
Примечание
Все числа должны быть целыми числами больше 0.
👇Свой вариант решения в комментарии
@python_job_interview
🖥 Задача Единственный выживший
Это вариант классической задачи Иосифа Флавия. В кругу стоят n человек, пронумерованных числами от 1 до n. Начинается расчет, при котором каждый k-й по счету человек выбывает из круга, после чего счет продолжается со следующего за ним человека. Напишите программу, определяющую номер человека, который останется в кругу последним.
Входные данные:
Числа n и k на отдельных строках.
#Пример ввода9 3
Выходные данные:
Номер последнего оставшегося человека.
#Пример вывода1
Решение
Способ 1:
n, k = int(input()), int(input())
last = 0
for i in range(1, n + 1):
last = (last + k) % i
print(last + 1)
Способ 2 – рекурсия:
def lastSurvivor(n, k):
if n == 1:
return 1
elif n > 1:
return (1 + (lastSurvivor(n - 1, k) + k - 1) % n)
n, k = int(input()), int(input())
print(lastSurvivor(n, k))
@python_job_interview
🖥 Полный список вопросов с собеседований по Python для дата-сайентистов.
Большинство исследователей данных пишут много кода, поэтому такой список пригодится и дата-сайентистам, и инженерам. Он будет полезен и для соискателей, и для тех, кто проводит собеседования, и для тех, кто просто изучает Python.
✔️ Читать дальше
@machinelearning_interview
🖥 Задача с собеседования Junior
Условие:
Ваша задача — написать функцию, которая принимает неограниченное количество списков и возвращает только те элементы, что есть в каждом списке.
Пример:find_values([11, 10, 3], [10, 3, 5, 11], [11, 10]) -> [11, 10]
find_values([8, 4, 7, "hi"], [8, "hi"], [4, "hi"]) -> ['hi']
find_values([1, 4, 3], [6, 2, 8], ["4", "hi"]) -> []
✔️ Пишите свои решения в комментариях👇
@python_job_interview
🖥 Как работают метаклассы
- перехватить создание класса
- изменить класс
- вернуть модифицированный
Зачем вообще использовать метаклассы
Основное применение метаклассов это создание API. Типичный пример — Django ORM.
Она позволяет написать что-то в таком духе:class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
Однако если вы выполните следующий код:
guy = Person(name='bob', age='35')
print guy.age
вы получите не IntegerField, а int, причём значение может быть получено прямо из базы данных.
Это возможно, потому что models.Model определяет __metaclass__, который сотворит некую магию и превратит класс Person, который мы только что определили простым выражением в сложную привязку к базе данных.
Django делает что-то сложное выглядящее простым, выставляя наружу простой API и используя метаклассы, воссоздающие код из API и незаметно делающие всю работу.
@python_job_interview
🖥 Что такое type. Как работает поиск метакласса при создании объекта?
type это метакласс, который Питон внутренне использует для создания всех классов.
Когда вы пишете:class Foo(Bar):
pass
Питон делает следующее:
- Есть ли у класса Foo атрибут __metaclass__?
- Если да, создаёт в памяти объект-класс с именем Foo, используя то, что указано в __metaclass__.
- Если Питон не находит metaclass, он ищет __metaclass__ в родительском классе Bar и попробует сделать то же самое.
- Если же __metaclass__ не находится ни в одном из родителей, Питон будет искать __metaclass__ на уровне модуля.
- И если он не может найти вообще ни одного __metaclass__, он использует type для создания объекта-класса.
@python_job_interview
🖥 Что такое сцепление исключений ?
В Python 3 при возбуждении исключения в блоке except, старое исключение сохраняется в атрибуте данных context и если новое исключение не обработано, то будет выведена информация о том, что новое исключение возникло при обработке старого («During handling of the above exception, another exception occurred:»). Также, можно связывать исключения в одну цепь или заменять старые новыми. Для этого используется конструкция raise новое_исключение from старое_исключение либо raise новое_исключение from None. В первом случае указанное исключение сохраняется в атрибуте __cause__ и атрибут __suppress_context__ (который подавляет вывод исключения из __context__) устанавливается в True. Тогда, если новое исключение не обработано, будет выведена информация о том, что старое исключение является причиной нового («The above exception was the direct cause of the following exception:»). Во втором случае __suppress_context__ устанавливается в True и __cause__ в None. Тогда при выводе исключения оно, фактически, будет заменено новым (хотя старое исключение всё ещё хранится в __context__).
В Python 2 нет сцепления исключений. Любое исключение, выброшенное в блоке except, заменяет старое.
@python_job_interview
🖥 Что такое MRO ?
MRO – method resolution order, порядок разрешения методов. Алгоритм, по которому следует искать метод в случае, если у класса два и более родителей.
В классических классах поиск при наследовании по ссылкам на имена осуществляется в следующем порядке:
1. Сначала экземпляр
2. Затем его класс
3. Далее все суперклассы его класса с обходом сначала с глубину, а затем слева направо
Используется первое обнаруженное вхождение. Такой порядок называется DFLR (Обход вглубину и слева направо).
При наследовании классов нового стиля применяется правило MRO (порядок разрешения методов), т.е линеаризованный обход дерева классов, причем вложенный элемент наследования становится доступным в атрибуте mro данного класса. Такой алгорим называется C3-линеаризация. Наследование по правилу MRO осуществляется приблизительно в следующем порядке.
Перечисление всех классов, наследуемых экземпляром, по правилу поиска DFLR для классических классов, причем класс включается в результат поиска столько раз, сколько он встречается при обходе.
Просмотр в полученном списке дубликатов классов, из которых удаляются все, кроме последнего (крайнего справа) дубликата в списке.
Упорядочение по правилу MRO применяется при наследовании и вызове встроенной функции super(), которая всегда вызывает следующий по правилу MRO класс (относительно точки вызова).
Пример наследования в неромбовидных иерархаических деревьях:class attr = 3 # D:3 E:2
class B(D) pass # | |
class E: attr = 2 # B C:1
class C(E): attr = 1 # / /
class A(B, C): pass # A
X = A() # |
print(X.attr) # X
# DFLR = [X, A, B, D, C, E]
# MRO = [X, A, B, D, C, E, object]
# И в версии 3.х и в версии 2.х (всегда) выводит строку "3"
Пример наследования в ромбовидных иерархаических деревьях:class attr = 3 # D:3 D:3
class B(D) pass # | |
class C(D): attr = 1 # B C:1
class A(B, C): pass # / /
X = A() # A
print(X.attr) # |
# X
# DFLR = [X, A, B, D, C, D]
# MRO = [X, A, B, C, D, object] (сохраняет только последний дубликат D)
# Выводит строку "1" в версии 3.х, строку "3" в версии 2.х ("1" если D(object))
@python_job_interview
🖥 Что особенного в генераторе?
Генератор хранит в памяти не все элементы, а только внутреннее состояние для вычисления очередного элемента. На каждом шаге можно вычислить только следующий элемент, но не предыдущий. Пройти генератор в цикле можно только один раз.
Как объявить генератор
использовать синтаксис (x for x in seq)
оператор yield в теле функции вместо return
встроенная функция iter, которая вызывает у объекта метод __iter__(). Этот метод должен возвращать генератор.
Как получить из генератора список
Передать его в конструктор списка: list(x for x in some_seq). Важно, что после этого по генератору уже нельзя будет итерироваться.
Что такое подгенератор
В Python 3 существуют так называемые подгенераторы (subgenerators). Если в функции-генераторе встречается пара ключевых слов yield from, после которых следует объект-генератор, то данный генератор делегирует доступ к подгенератору, пока он не завершится (не закончатся его значения), после чего продолжает своё исполнение.
На самом деле yield является выражением. Оно может принимать значения, которые отправляются в генератор. Если в генератор не отправляются значения, результат данного выражения равен None.yield from также является выражением. Его результатом является то значение, которое подгенератор возвращает в исключении StopIteration (для этого значение возвращается при помощи ключевого слова return).
@python_job_interview
🖥 Что такое итерабельный объект?
Итерабельный объект (в оригинальной терминологии – «iterable») – это объект, который может возвращать значения по одному за раз. Примеры: все контейнеры и последовательности (списки, строки и т.д.), файлы, а также экземпляры любых классов, в которых определён метод __iter__() или __getitem__(). Итерабельные объекты могут быть использованы внутри цикла for, а также во многих других случаях, когда ожидается последовательность (функции sum(), zip(), map() и т.д.).
Подробнее:
Рассмотрим итерируемый объект (Iterable). В стандартной библиотеке он объявлен как абстрактный класс collections.abc.Iterable:class Iterable(metaclass=ABCMeta):У него есть абстрактный метод
__slots__ = ()
@abstractmethod
def __iter__(self):
while False:
yield None
@classmethod
def __subclasshook__(cls, C):
if cls is Iterable:
return _check_methods(C, "__iter__")
return NotImplemented__iter__ который должен вернуть объект итератора. И метод __subclasshook__ который проверяет наличие у класса метод __iter__. Таким образом, получается, что итерируемый объект это любой объект который реализует метод __iter__class SomeIterable1(collections.abc.Iterable):Но есть один момент, это функция
def __iter__(self):
pass
class SomeIterable2:
def __iter__(self):
pass
print(isinstance(SomeIterable1(), collections.abc.Iterable))
# True
print(isinstance(SomeIterable2(), collections.abc.Iterable))
# Trueiter(). Именно эту функцией использует например цикл for для получения итератора. Функция iter() в первую очередь для получения итератора из объекта, вызывает его метод __iter__. Если метод не реализован, то она проверяет наличие метода __getitem__ и если он реализован, то на его основе создается итератор. __getitem__ должен принимать индекс с нуля. Если не реализован ни один из этих методов, тогда будет вызвано исключение TypeError.from string import ascii_letters
class SomeIterable3:
def __getitem__(self, key):
return ascii_letters[key]
for item in SomeIterable3():
print(item)
@python_job_interview
🖥 Как передаются значения аргументов в функцию или метод
Интересности и полезности python. Часть 3
В таких языках как C++ есть переменные, хранящиеся на стеке и в динамической памяти. При вызове функции мы помещаем все аргументы на стек, после чего передаём управление функции. Она знает размеры и смещения переменных на стеке, соответственно может их правильно интерпретировать. При этом у нас есть два варианта: скопировать на стек память переменной или положить ссылку на объект в динамической памяти (или на более высоких уровнях стека). Очевидно, что при изменении значений на стеке функции, значения в динамической памяти не поменяются, а при изменении области памяти по ссылке, мы модифицируем общую память, соответственно все ссылки на эту же область памяти «увидят» новое значение.
В python отказались от подобного механизма, заменой служит механизм связывания (assignment) имени переменной с объектом, например при создании переменной: var = "john"
Интерпретатор создаёт объект «john» и «имя» var, а затем связывает объект с данным именем. При вызове функции, новых объектов не создаётся, вместо этого в её области видимости создаётся имя, которое связывается с существующим объектом. Но в python есть изменяемые и неизменяемые типы. К первым, например, относятся числа: при арифметических операциях существующие объекты не меняются, а создаётся новый объект, с которым потом связывается существующее имя. Если же со старым объектом после этого не связано ни одного имени, оно будет удалено с помощью механизма подсчёта ссылок. Если же имя связано с переменной изменяемого типа, то при операциях с ней изменяется память объекта, соответственно все имена, связанные с данной областью памяти «увидят» изменения.
@python_job_interview