Python вопросы с собеседований
24.8K subscribers
510 photos
16 videos
17 files
407 links
Вопросы с собеседований по Python

@workakkk - админ

@machinelearning_interview - вопросы с собесдований по Ml

@pro_python_code - Python

@data_analysis_ml - анализ данных на Python

@itchannels_telegram - 🔥 главное в ит

РКН: clck.ru/3FmrFd
Download Telegram
🖥 Как производится отладка программы на 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
Please open Telegram to view this post
VIEW IN TELEGRAM
🖥 Что такое перегрузка операторов? Есть ли перегрузка операторов в 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
Please open Telegram to view this post
VIEW IN TELEGRAM
🖥 Как устроено преобразование типов в Python ?

Преобразование типов - это преобразование объекта из одного типа данных в другой тип данных.

Неявное преобразование типов автоматически выполняется интерпретатором Python.

Python позволяет избежать потери данных в неявном преобразовании типов.

Явное преобразование типов также называется приведением типов, типы данных объекта преобразуются с использованием предопределенной функции.

При приведении типов может произойти потеря данных, поскольку мы приводим объект к определенному типу данных.

Что такое аннотация типов?

В простейшем случае аннотация содержит непосредственно ожидаемый тип. Аннотации для переменных пишут через двоеточие после идентификатора. После этого может идти инициализация значения. Например price: int = 5 Параметры функции аннотируются так же как переменные, а возвращаемое значение указывается после стрелки -> и до завершающего двоеточия. Например def indent_right(s: str, width: int) -> str:.

@python_job_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
✔️Задача | #Junior

Условие:

Вам нужно написать функцию, которая будет обрезать строку до определённой длины и добавлять в конец троеточие. Если текст равен max_len или max_len больше длины текста, то ничего не меняем.

Пример:

text = "Lorem Ipsum is simply dummy text"
cut_str(text, max_len=12) -> Lorem Ipsum...
cut_str(text, max_len=40) -> Lorem Ipsum is simply dummy text


❗️Делитесь своим решением в комментариях👇

@python_job_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
✔️Задача | #Middle


Реализуйте итератор колоды карт (52 штуки) CardDeck. Каждая карта представлена в виде строки типа «2 Пик». При вызове функции next() будет представлена следующая карта. По окончании перебора всех элементов возникнет ошибка StopIteration.

Ответ
Чтобы реализовать протокол итератора требуется внедрить 2 метода: __iter__() и __next__(). Для понимания того, что коллекция иссякла, не стоить забывать и про ее длину (52).

class CardDeck:
def __init__(self):
self.length = 52
self.index = 0
self.__SUITS = ['Пик', 'Бубей', 'Червей', 'Крестей']
self.__RANKS = [*range(2, 11), 'J', 'Q', 'K', 'A']

def __len__(self):
return self.length

def __next__(self):
if self.index >= self.length:
raise StopIteration
else:
suit = self.__SUITS[self.index // len(self.__RANKS)]
rank = self.__RANKS[self.index % len(self.__RANKS)]
self.index += 1
return f'{rank} {suit}'

def __iter__(self):
return self


deck = CardDeck()
while True:
print(next(deck))

Результат выполнения

2 Пик
3 Пик
4 Пик

K Крестей
A Крестей
StopIteration


@python_job_interview
Быть DevOps специалистом в 2023 году — значит обладать самой востребованной ит-профессией. Зайдите в гугл и убедитесь - на рынке нехватка спецов, больше 5000 компаний ищут DevOps разработчиков, а средняя зарплата в DevOps — 250 000 рублей.

Стоит ли читать книги или покупать курсы? Однозначно Нет.
С каналом DevOps school вы за пару месяц освоите профессию.

В канале автор понятным языком рассказывает, как быстро освоить ключевые навыки на практике, почему Devops - золотая ниша в ит какие тренды изучать в 2023 году

Подписывайтесь: @DevOPSitsec
⚡️ Задача с собеседования |: #Junior

Условие:

Ваша задача — написать функцию, которая находит сумму всех цифр в числе. На вход также могут пойти и числа меньше нуля — их стоит переводить в неотрицательное числа.

Пример:

1234 → 10
-9876 → 30
7013 → 11
100001 → 2


Знак "_" в числах может использоваться для упрощения чтения пользователем. 1_000_000 — это то же самое, что и 1000000

👉 Пишите ваше решение в комментариях👇

@python_job_interview
⚡️ Задача с собеседования |: #Junior

Условие

Числа Фибоначчи представляют последовательность, получаемую в результате сложения двух предыдущих элементов.
Начинается коллекция с чисел 1 и 1.
Она достаточно быстро растет, поэтому вычисление больших значений занимает немало времени.
Создайте функцию fib(n), генерирующую n чисел Фибоначчи с минимальными затратами ресурсов.
Для реализации этой функции потребуется обратиться к инструкции yield.
Она не сохраняет в оперативной памяти огромную последовательность, а дает возможность “доставать” промежуточные результаты по одному.

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

Решение

def fib(n):
fib0 = 1
yield fib0
fib1 = 1
yield fib1
for i in range(n - 2):
fib0, fib1 = fib1, fib0 + fib1
yield fib1


# Тест
for num in fib(112121):
pass
print(num)


👉 Пишите свое решение в комментариях👇

@python_job_interview
⚡️ Задача

Требуется создать csv-файл «rows_300.csv» со следующими столбцами:

– № - номер по порядку (от 1 до 300);
– Секунда – текущая секунда на вашем ПК;
– Микросекунда – текущая миллисекунда на часах.

На каждой итерации цикла искусственно приостанавливайте скрипт на 0,01 секунды.
Для работы с файлами подобного текстового формата потребуется встроенная в Python библиотека csv.

Решение

import csv
import datetime
import time

with open('rows_300.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(['№', 'Секунда ', 'Микросекунда'])
for line in range(1, 301):
writer.writerow([line, datetime.datetime.now().second, datetime.datetime.now().microsecond])
time.sleep(0.01)


В итоге создастся требуемый файл. Приведем первые строки его содержимого:
Содержимое файла rows_300.csv

№,Секунда ,Микросекунда
1,51,504807
2,51,515807
3,51,526819
4,51,537817
5,51,548800
6,51,558817


👉 Пишите ваше решение в комментариях👇


@python_job_interview
Когда применяются ленивые вычисления и в чем их логика

Ленивые вычисления предполагают, что не нужно ничего делать до тех пор, пока в этом нет необходимости. Так, если мы не обратились к свойству класса (которое определяется некими математическими операциями, например), то не нужно заранее его рассчитывать. На этом базируется, в том числе, логика генераторов. Многие итераторы также являются ленивыми. Это не просто удобно, но позволяет экономить память и время на вычисление.

Нагляднее всего можно понять преимущество ленивых вычислений (и итераторов) на следующем примере.

# Создадим итерабельный объект

nums = range(1, 12121212112)
nums[10543210010]
10543210011 # Практически моментальный вывод
list(nums)
MemoryError


Функция range()– ленивая. Она заранее не формирует последовательность из гигантского количества чисел. А когда мы обращаемся к некому ее элементу по индексу (это разрешено, так как она создает итерабельный объект), он выводится почти сразу.

Если вы рискнете преобразовать этот массив чисел в список, то случится одно из двух: либо выведется ошибка о нехватке памяти на компьютере, либо очень-очень долго будет формироваться список всех объектов.

@python_job_interview
Что такое дескрипторы данных?

Очень часто переменные, инициализируемые в классе, являются однотипными. Например, есть класс Employee (сотрудник), принимающий параметры: имя, фамилия, отчество, должность. Все они являются строками. Следовательно, прежде чем создать экземпляр класса, нужно проверить, что пользователь ввел строки. А для этого потребуются сеттеры, проверяющие тип вводимых параметров. В итоге, мы 4 раза повторим код проверки. Нарушается принцип DRY (don't repeat yourself).

Для таких ситуаций удобно использовать дескрипторы (они, к слову, широко применяются во фреймворке Django при создании моделей).

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

Существует 4 метода протокола дескрипторов:
get__() - получить значение свойства;
set__() - задать значение;
delete__() - удалить атрибут;
set_name__() - присвоить имя свойству (появился в Питоне версии 3.6).

Если применяется только метод __get__(), то мы имеем дело с дескриптором без данных, а если есть еще и __set__(), то речь будет идти о дескрипторе данных.

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

Пример – IDE
---
# Создаем класс с протоколами дескриптора
class StringChecker:

# Получаем доступ к свойству
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]

# Меняем свойство
def __set__(self, instance, str_value):
if not isinstance(str_value, str):
raise ValueError('Нужно предоставить строку')
elif len(str_value) < 2:
raise ValueError('Необходимо минимум 2 буквы')
instance.__dict__[self.name] = str_value

# Задаем имя свойства
def __set_name__(self, owner, name):
self.name = name


class Employee:


# Определяем атрибуты (их может быть любое количество)
name = StringChecker()
surname = StringChecker()
patronymic = StringChecker()
post = StringChecker()

# Инициализируем свойства с учетом требуемых проверок
def __init__(self, name, surname, patronymic, post):
self.name = name
self.surname = surname
self.patronymic = patronymic
self.post = post



# Тесты
director = Employee('Иван', 'Николаевич', 'Прогин', 'Директор')
print(director.__dict__)
director.name = 1
director.name = 'A'



Результат выполнения
---
{'name': 'Иван', 'surname': 'Николаевич', 'patronymic': 'Прогин', 'post': 'Директор'}
ValueError: Нужно предоставить строку
ValueError: Минимум две буквы в атрибуте требуется

@python_job_interview
⚡️ Задача

Инструкция yield позволяет создавать генераторы.

Инструкция yield позволяет создавать генераторы.
В отличие от объявления return в функции, где возвращается один объект, yield при каждом вызове функции генерирует новый объект.

Фактически это дает возможность использовать генераторы в циклах.
Самая важная причина применения такой инструкции - экономия памяти, когда не требуется сохранять всю последовательность, а можно получать ее элементы по одному.

Ученик написал генератор show_letters(some_str), выводящий все символы строки на печать, но только в том случае, если они являются буквами (остальные игнорируются).
Сократите код функции.

Код
---
def show_letters(some_str):
clean_str = ''.join([letter for letter in some_str if letter.isalpha()])
for symbol in clean_str:
yield symbol

Конструкция yield from позволяет полностью убрать цикл из функции. Она "вкладывает" один генератор внутрь другого, что дает возможность управления несколькими генераторами.

Решение - IDE

def show_letters(some_str):
yield from ''.join([letter for letter in some_str if letter.isalpha()])


random_str = show_letters('A!sdf 09 _ w')
print(next(random_str))
print(next(random_str))


Результат выполнения

A
s


@python_job_interview
⚡️ Задача: найдите первый неповторяющийся символ в строке, выполнив только один обход

Для заданной строки найдите в ней первый неповторяющийся символ, выполнив только один ее обход.

Например,

Input:

string is ABCDBAGHC

Output:

первый неповторяющийся символ: D

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

Затем еще раз просмотреть строку, чтобы найти первый символ, имеющий значение 1. Временная сложность этого решения равна O(n), где n длина входной строки. Проблема с этим решением заключается в том, что строка проходится дважды, что нарушает ограничения программы.

Мы можем решить эту задачу за один обход строки. Идея состоит в том, чтобы использовать словарь для хранения количества каждого отдельного символа и индекса его первого или последнего вхождения в строку. Затем пройтись по словарю и найти символ с минимальным индексом строки.

# Функция поиска первого неповторяющегося символа в
# строк
е, выполнив только один ее обход
def findNonRepeatingChar(s):

# Базовый вариант
if not s:
return -1

# словарь # для хранения количества символов и индекса их
# последнее вхождение в строку
d = {}

for index, char in enumerate(s):
frequency, prevIndex = d.get(char, (0, index))
d[char] = (frequency + 1, index)

# хранит индекс первого неповторяющегося символа
min_index = -1

# Проходим словарь и находим символ
for key, values in d.items():
count, firstIndex = values
if count == 1 and (min_index == -1 or firstIndex < min_index):
min_index = firstIndex

return min_index


if __name__ == '__main__':

s = 'ABCDBAGHC'

index = findNonRepeatingChar(s)
if index != -1:
print('первый неповторяющийся символ: ', s[index])
else:
print('Таких символов нет')


👉 Пишите ваше решение в комментариях👇

@python_job_interview
⚡️ Что такое coroutine (Сопрограмма) в Python?

Сопрограмма (coroutine) — результат вызова асинхронной функции, представляющий собой выполнение этой функции, способное приостанавливаться. Так как в общем случае невозможно определить сколько раз и на какое время выполнение будет приостановлено, невозможно и сказать когда оно будет закончено. Ваш код может либо ждать завершения выполнения сопрограммы с помощью оператора await, либо поручить ожидание циклу событий и продолжить свой выполнение.

В первом случае

async def callee():
print('Hello')

async def caller():
await callee()
print('World')


выполнение caller приостановится до выполнения callee. В этот момент какие-то другие операции в каких-то других сопрограммах могут продолжаться, но caller будет ждать там, где выполнил await.

Во втором случае

async def callee():
print('Hello')

async def caller():
asyncio.create_task(callee())
print('World')


caller сразу же продолжит свою работу. Строка "World" будет выведена раньше, чем "Hello". Здесь мы видим, что caller поставил циклу событий задачу выполнить сопрограмму callee.

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

Футура (Future) - будущий результат выполнения сопрограммы. Метод ensure_future поручает циклу событий выполнить сопрограмму и сразу же, в момент вызова, возвращает футуру, в которой будет значение, но неизвестно когда. Вызывающая сторона может подождать выполнения футуры так же, как ожидало саму сопрограмму

async def callee():
return 'Hello'

async def caller():
loop = asyncio.get_event_loop()
future = loop.ensure_future(callee())
result = await future
print(result + ' World')


Или может заняться своими делами, периодически проверяя готовность

async def caller():
loop = asyncio.get_event_loop()
future = loop.ensure_future(callee())
while not future.done():
# Какие-нибудь циклические дела
print(future.result() + ' World')
Или установить на футуру колбэк

async def caller():
loop = asyncio.get_event_loop()
future = loop.ensure_future(callee())
future.add_done_callback(lambda f: print(f.result() + ' World'))
# какие-нибудь другие важные дела


Или может собрать их в список и ждать все. Или не все, а только ту, которая будет выполнена первой, а остальные проигнорировать. Или передать футуру другой сопрограмме, а самой заняться каким-нибудь другим делом. В общем, это "очень полезный горшок, куда можно класть какие хочешь вещи".

Осталось только разобраться, чем отличаются футуры от задач. Ничем не отличаются, по большому счёту. Класс Task - это наследник класса Future. А существенная разница между asyncio.create_task() и loop.ensure_future() только в том, что первой не было до Python 3.7.

Подытоживая: Task - это задача, поставленная циклу событий, на выполнение coroutine, одновременно являющаяся Future, которая представляет собой результат выполнения Task когда-нибудь в будущем.

@python_job_interview
⚡️ Что такое интроспекция?

Introspection in Python
Интроспекция — это способность программы исследовать тип или свойства объекта во время работы программы. Вы можете поинтересоваться, каков тип объекта, является ли он экземпляром класса. Некоторые языки даже позволяют узнать иерархию наследования объекта. Возможность интроспекции есть в таких языках, как Ruby, Java, PHP, Python, C++ и других. В целом, инстроспекция — это очень простое и очень мощное явление. Вот несколько примеров использования инстроспекции:

// Java

if(obj instanceof Person){
Person p = (Person)obj;
p.walk();
}
//PHP

if ($obj instanceof Person) {
// делаем что угодно
}

В Python самой распространённой формой интроспекции является использование метода dir для вывода списка атрибутов объекта:

# Python

class foo(object):
def __init__(self, val):
self.x = val
def bar(self):
return self.x

...

dir(foo(5))
=> ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'bar', 'x']


@python_job_interview
🖥 Что такое рефлексия?

Интроспекция позволяет вам изучать атрибуты объекта во время выполнения программы, а рефлексия — манипулировать ими.

Рефлексия — это способность компьютерной программы изучать и модифицировать свою структуру и поведение (значения, мета-данные, свойства и функции) во время выполнения.

Простым языком: она позволяет вам вызывать методы объектов, создавать новые объекты, модифицировать их, даже не зная имён интерфейсов, полей, методов во время компиляции.

Из-за такой природы рефлексии её труднее реализовать в статически типизированных языках, поскольку ошибки типизации возникают во время компиляции, а не исполнения программы (подробнее об этом здесь). Тем не менее, она возможна, ведь такие языки, как Java, C# и другие допускают использование как интроспекции, так и рефлексии (но не C++, он позволяет использовать лишь интроспекцию).

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

Пример:

# Без рефлексии
Foo().hello()

# С рефлексией
getattr(globals()['Foo'](), 'hello')()

@python_job_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
🖥 Собеседование на должность QA Automation Engineer. Python

Полный список вопрос и ответов.

📌 Читать


@python_job_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
🖥Задача: Middle

Строки в Питоне сравниваются на основании значений символов. Т.е. если мы захотим выяснить, что больше: «Apple» или «Яблоко», – то «Яблоко» окажется бОльшим.

А все потому, что английская буква «A» имеет значение 65 (берется из таблицы кодировки), а русская буква «Я» – 1071 (с помощью функции ord() это можно выяснить).
Такое положение дел не устроило программиста.

Он считает, что строки нужно сравнивать по количеству входящих в них символов.

Для этого программист создал класс RealString и реализовал озвученный инструментарий. Сравнивать между собой можно как объекты класса, так и обычные строки с экземплярами класса RealString.

К слову, программисту понадобилось только 3 метода внутри класса (включая __init__()) для воплощения задуманного.

В общем случае для создания такого класса понадобится 4 метода, так как в Питоне реализованы «богатые» сравнения. Это значит, что если имеется сравнение «больше», то автоматом появится возможность осуществлять сравнение «меньше».

Решение
class RealString:
def __init__(self, some_str):
self.some_str = str(some_str)

def __eq__(self, other):
if not isinstance(other, RealString):
other = RealString(other)
return len(self.some_str) == len(other.some_str)

def __lt__(self, other):
if not isinstance(other, RealString):
other = RealString(other)
return len(self.some_str) < len(other.some_str)

def __le__(self, other):
return self == other or self < other

Чтобы повторить класс, придуманный программистом (с тремя методами), требуется воспользоваться декоратором @total_ordering из модуля functools (упрощает реализацию сравнений. Требует лишь 2 дополняющих варианта сравнения - например, больше и равно - чтобы автоматически "дописать" остальные).

from functools import total_ordering


@total_ordering
class RealString:
def __init__(self, some_str):
self.some_str = str(some_str)

def __eq__(self, other):
if not isinstance(other, RealString):
other = RealString(other)
return len(self.some_str) == len(other.some_str)

def __lt__(self, other):
if not isinstance(other, RealString):
other = RealString(other)
return len(self.some_str) < len(other.some_str)


# Тесты
str1 = RealString('Молоко')
str2 = RealString('Абрикосы растут')
str3 = 'Золото'
str4 = [1, 2, 3]
print(str1 < str4)
print(str1 >= str2)
print(str1 == str3)


Вывод:

True
False
True


@python_job_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
🖥 Задача о циклическом сдвиге

Циклический сдвиг массива — это когда каждый элемент кроме последнего сдвигается вправо, а последний элемент массива становится первым.

На вход подаются массив A и целое число K. Сделайте циклический сдвиг входного массива K раз и верните получившийся массив.

Например, для данных:

A = [2, 5, 1, 4, 6]
K = 3


Циклический сдвиг должен быть выполнен трижды:

[2, 5, 1, 4, 6] -> [6, 2, 5, 1, 4]
[6, 2, 5, 1, 4] -> [4, 6, 2, 5, 1]
[4, 6, 2, 5, 1] -> [1, 4, 6, 2, 5]


И программа должна вернуть

[1, 4, 6, 2, 5]

Если K = 0, то сдвиг не делается.

Для K = 5:

[2, 5, 1, 4, 6] -> [2, 5, 1, 4, 6]

Наивное решение:

1. Определяем, что такое ротировать массив один раз.
2. Делаем это K раз.

def rotate(A, K):
""" Rotates a list K times """
if not A:
return []

for i in range(K):
A = rotate_once(A)
return A


def rotate_once(A):
""" Rotates a list once """
A.insert(0, A[-1])
del A[-1]
return A

Это решение не оптимальное, потому что каждый insert() передвигает все элементы исходного списка. Также оно не учитывает, что после len(A) ротаций список возвращается в исходное состояние.

Индексы элементов в новом массиве несложно рассчитать аналитически, поэтому:

def rotate(A, K):
""" Rotates a list K times """
result = []
if K == 0:
return A
else:
for i in range(len(A)):
new_index = (i - K) % len(A)
result.append(A[new_index])
return result


Это лучше, потому что
результат получается за один проход
и время работы не увеличивается на больших K.

@python_job_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
📌 Задача с собеседования | Уровень: #Easy

Условие:

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

Пример:
Ф-ция принимает:
"I learn Python with Python Job"

Ф-ция возвращает: "9 12 5 1 18 14 16 25 20 8 15 14 23 9 20 8 16 25 20 8 15 14 10 15 2"

Решение
import string


def str_to_int(s: str = ''):
if not s:
return 'входная строка пустая'
alphabet = list(string.ascii_lowercase)
position_in_alphabet = [str(i + 1) for i in range(len(alphabet))]
dict_alphabet = dict(zip(alphabet, position_in_alphabet))
s_to_i = ''
for char in s.lower():
if char in dict_alphabet:
s_to_i += dict_alphabet.get(char) + ' '
return s_to_i


print(str_to_int('I learn Python with Python Job'))

👉 Пишите ваше решение в комментариях👇

@python_job_interview
📌 Задача

У продавца и покупателя имеется неограниченное кол-во монет достоинством (1,2,5,10,20,50,100,200,500).
Покупатель купил товар на сумму n. Hужно найти
минимальное кол-во монет, которые будут использованы при расплате. Деньги может давать как покупатель, так и продавец.

Решение
a = input('Введите сумму: ')

d = dict.fromkeys([500, 200, 100, 50, 20, 10, 5, 2, 1], 0)

def get_nearest_value(iterable, value):
return min(iterable, key=lambda x: abs(x - value))

for i in d.keys():
d[i] = _i = int(a)/i
a = int(a)%i

_key = get_nearest_value(d.keys(), a)
d[_key] += 1

for k in d.keys():
print('%s - %d' % (k, d[k]))


Какая временная сложность данного алгоритма?

👉 Пишите ваше решение в комментариях👇

@python_job_interview