ООП на собеседовании по Python — почти гарантированная тема для позиций Middle и выше, а для Junior — базовый уровень: классы, объекты, self, наследование. Разберём вопросы, которые звучат чаще всего, с кодом и объяснением, на что смотрит интервьюер.
Класс, объект и self
Как звучит на собесе: «Напиши класс и объясни, что такое self».
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def give_raise(self, amount):
self.salary += amount
anna = Employee('Анна', 100000)
anna.give_raise(20000)
print(anna.salary) # 120000
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def give_raise(self, amount):
self.salary += amount
anna = Employee('Анна', 100000)
anna.give_raise(20000)
print(anna.salary) # 120000
self — это ссылка на конкретный экземпляр класса, через который методы получают доступ к его данным. Когда вы пишете anna.give_raise(20000), Python автоматически передаёт anna как первый аргумент метода — self. Без self метод не знал бы, чьи именно данные менять: self.salary у anna и self.salary у другого сотрудника — разные значения, хранящиеся в разных объектах.
На что смотрит интервьюер: понимаете ли вы, что self — не магическое ключевое слово, а обычный параметр, который получает ссылку на конкретный объект.
На что смотрит интервьюер: понимаете ли вы, что self — не магическое ключевое слово, а обычный параметр, который получает ссылку на конкретный объект.
Наследование
Как звучит на собесе: «Что такое наследование и зачем оно нужно?»
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def describe(self):
return f'{self.name}, зарплата {self.salary}'
class Manager(Employee):
def __init__(self, name, salary, team_size):
super().__init__(name, salary)
self.team_size = team_size
def describe(self):
base = super().describe()
return f'{base}, команда из {self.team_size} человек'
m = Manager('Борис', 200000, 5)
print(m.describe()) # Борис, зарплата 200000, команда из 5 человек
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def describe(self):
return f'{self.name}, зарплата {self.salary}'
class Manager(Employee):
def __init__(self, name, salary, team_size):
super().__init__(name, salary)
self.team_size = team_size
def describe(self):
base = super().describe()
return f'{base}, команда из {self.team_size} человек'
m = Manager('Борис', 200000, 5)
print(m.describe()) # Борис, зарплата 200000, команда из 5 человек
Manager наследует от Employee — получает его атрибуты и методы, не переписывая их заново. super().__init__(...) вызывает конструктор родительского класса — это позволяет не дублировать код инициализации name и salary. Метод describe() в Manager переопределён (override) — это называется полиморфизмом: одноимённый метод ведёт себя по-разному в зависимости от класса объекта.
На что смотрит интервьюер: умеете ли вы использовать super() вместо дублирования кода родителя, и понимаете ли разницу между наследованием (переиспользование) и переопределением метода (изменение поведения).
На что смотрит интервьюер: умеете ли вы использовать super() вместо дублирования кода родителя, и понимаете ли разницу между наследованием (переиспользование) и переопределением метода (изменение поведения).
Магические методы (dunder methods)
Как звучит на собесе: «Что такое __init__, __str__, __eq__? Зачем нужны методы с двойным подчёркиванием?»
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f'Point({self.x}, {self.y})'
def __eq__(self, other):
return self.x == other.x and self.y == other.y
p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1) # Point(1, 2) — использует __str__
print(p1 == p2) # True — использует __eq__, а не сравнение по id
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f'Point({self.x}, {self.y})'
def __eq__(self, other):
return self.x == other.x and self.y == other.y
p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1) # Point(1, 2) — использует __str__
print(p1 == p2) # True — использует __eq__, а не сравнение по id
Магические методы («дандеры», от double underscore) — это способ Python встроить объект в стандартное поведение языка. __init__ вызывается при создании объекта. __str__ определяет, что покажет print(). __eq__ определяет, что значит «равны» для двух объектов этого класса — без него == сравнивал бы объекты по идентичности (is), а не по содержимому.
На что смотрит интервьюер: знаете ли вы, что без __eq__ сравнение p1 == p2 вернуло бы False, даже если у объектов одинаковые x и y — потому что по умолчанию == для объектов работает как is.
На что смотрит интервьюер: знаете ли вы, что без __eq__ сравнение p1 == p2 вернуло бы False, даже если у объектов одинаковые x и y — потому что по умолчанию == для объектов работает как is.
Классовые атрибуты против атрибутов экземпляра
Как звучит на собесе: «В чём разница между атрибутом класса и атрибутом экземпляра?»
class Employee:
company = 'Quality Academy' # атрибут класса — общий для всех
def __init__(self, name):
self.name = name # атрибут экземпляра — свой у каждого
e1 = Employee('Анна')
e2 = Employee('Борис')
print(e1.company, e2.company) # Quality Academy Quality Academy
Employee.company = 'Другая компания'
print(e1.company, e2.company) # Другая компания Другая компания (общий для всех)
e1.name = 'Изменено'
print(e1.name, e2.name) # Изменено Борис (у каждого свой)
class Employee:
company = 'Quality Academy' # атрибут класса — общий для всех
def __init__(self, name):
self.name = name # атрибут экземпляра — свой у каждого
e1 = Employee('Анна')
e2 = Employee('Борис')
print(e1.company, e2.company) # Quality Academy Quality Academy
Employee.company = 'Другая компания'
print(e1.company, e2.company) # Другая компания Другая компания (общий для всех)
e1.name = 'Изменено'
print(e1.name, e2.name) # Изменено Борис (у каждого свой)
Атрибут класса (company) хранится один раз на весь класс и общий для всех экземпляров — если изменить его через класс, изменение видят все объекты. Атрибут экземпляра (name, заданный через self в __init__) — свой у каждого объекта, независимый от других.
На что смотрит интервьюер: понимаете ли вы, что изменение атрибута класса влияет на все экземпляры сразу — это частый источник неочевидных багов, если использовать изменяемые объекты (списки, словари) как атрибуты класса по умолчанию.
На что смотрит интервьюер: понимаете ли вы, что изменение атрибута класса влияет на все экземпляры сразу — это частый источник неочевидных багов, если использовать изменяемые объекты (списки, словари) как атрибуты класса по умолчанию.
Частые вопросы про ООП в Python
Что обязательно нужно знать про ООП для собеседования Junior Python? Базовый уровень: как объявить класс, что такое self, как работает __init__, простое наследование через super(). Middle-уровень добавляет магические методы, разницу атрибутов класса и экземпляра, множественное наследование.
Зачем нужен super() вместо прямого вызова родительского метода? super() корректно работает с множественным наследованием и порядком разрешения методов (MRO), тогда как прямой вызов родительского класса по имени менее гибкий и может сломаться при изменении иерархии классов.
Что будет, если не переопределить __eq__ у класса? Сравнение == будет работать как is — сравнивать объекты по идентичности в памяти, а не по содержимому атрибутов. Два объекта с одинаковыми данными будут считаться неравными, если это разные объекты в памяти.
В чём практическая опасность атрибутов класса? Если в качестве атрибута класса по умолчанию использовать изменяемый объект (список, словарь), изменения через один экземпляр отразятся на всех остальных — это тот же класс ошибок, что и с изменяемыми аргументами по умолчанию в функциях.
Зачем нужен super() вместо прямого вызова родительского метода? super() корректно работает с множественным наследованием и порядком разрешения методов (MRO), тогда как прямой вызов родительского класса по имени менее гибкий и может сломаться при изменении иерархии классов.
Что будет, если не переопределить __eq__ у класса? Сравнение == будет работать как is — сравнивать объекты по идентичности в памяти, а не по содержимому атрибутов. Два объекта с одинаковыми данными будут считаться неравными, если это разные объекты в памяти.
В чём практическая опасность атрибутов класса? Если в качестве атрибута класса по умолчанию использовать изменяемый объект (список, словарь), изменения через один экземпляр отразятся на всех остальных — это тот же класс ошибок, что и с изменяемыми аргументами по умолчанию в функциях.
Закрепить ООП в Python на практике
Разница между __eq__ по умолчанию и переопределённым — из тех вещей, которые легче понять на своём коде, чем в теории. Потренироваться можно на тренажёре Python Arena от Quality Academy — 1339 задач, значительная часть бесплатно.
-------
Полезные ссылки школы
Сайт Quality Academy:
https://quality-academy.ru/main
Telegram-канал:
https://t.me/quality_academy
YouTube-канал:
https://www.youtube.com/@quality_academy
ВКонтакте:
https://vk.com/quality_academy
Канал отзывов учеников (58+ отзывов):
https://t.me/quality_academy_reviews
Задать вопрос менеджеру:
https://t.me/quality_academy_bot
Тест «Подойдёт ли вам тестирование»:
https://quiz.quality-academy.ru
5000+ вопросов с собесов тестировщика — бот-тренажёр:
https://t.me/quality_academy_interview_bot
3 практические задачи и дорожная карта:
https://t.me/quality_academy_tasks_bot
Бесплатные тренажёры — SQL Arena, Playwright Arena, Python Arena:
SQL Arena · Playwright Arena · Python Arena
https://quality-academy.ru/main
Telegram-канал:
https://t.me/quality_academy
YouTube-канал:
https://www.youtube.com/@quality_academy
ВКонтакте:
https://vk.com/quality_academy
Канал отзывов учеников (58+ отзывов):
https://t.me/quality_academy_reviews
Задать вопрос менеджеру:
https://t.me/quality_academy_bot
Тест «Подойдёт ли вам тестирование»:
https://quiz.quality-academy.ru
5000+ вопросов с собесов тестировщика — бот-тренажёр:
https://t.me/quality_academy_interview_bot
3 практические задачи и дорожная карта:
https://t.me/quality_academy_tasks_bot
Бесплатные тренажёры — SQL Arena, Playwright Arena, Python Arena:
SQL Arena · Playwright Arena · Python Arena