Наследование классов: неправильный вывод
Столкнулся с проблемой неправильного вывода результата наследуемого метода.
Задача:
Создайте базовый класс Specialist, сохраняющий атрибут экземпляра name имеющий методы drink_coffee, возвращающий строку "{имя} пьет кофе", и work, возвращающий строку вида "{имя} выполняет работу". Создайте дочерний класс Coder, наследующий методы и атрибуты класса Specialist, и переопределяющий метод work таким образом, чтобы он возвращал строку вида "{имя} пишет код". Создайте дочерний класс ProjectManager, наследующий методы и атрибуты класса Specialist, и переопределяющий метод work таким образом, чтобы он возвращал строку вида "{имя} управляет проектом". Создайте класс TeamLeader, наследующий атрибуты и методы классов Coder и ProectManager таким образом, чтобы при вызове метода work возвращалась строка "{имя} управляет проектом". Проверка этого критерия будет производиться при помощи инструмента MRO
Мой код:
class Specialist:
def __init__(self, name):
self.name = name
# Метод drink_coffee
def drink_coffee(self):
return f"{self.name} пьет кофе"
# Базовый метод work
def work(self):
return f"{self.name} выполняет работу"
# Дочерний класс Coder, наследующий от Specialist
class Coder(Specialist):
# Переопределяем метод work
def work(self):
return f"{self.name} пишет код"
# Дочерний класс ProjectManager, наследующий от Specialist
class ProjectManager(Specialist):
# Переопределяем метод work
def work(self):
return f"{self.name} управляет проектом"
# Класс TeamLeader, наследующий от Coder и ProjectManager
class TeamLeader(Coder, ProjectManager):
# Переопределять метод work не нужно, он будет взят по MRO из ProjectManager
pass
Тест который должен проходить:
t = TeamLeader('Василий')
print(t.work())
Правильный вывод:
Василий управляет проектом
Мой неправильный вывод:
Василий пишет код
Ответы (2 шт):
Для решения вашей проблемы, нужно сперва разобраться с порядком разрешения методов (method resolution order). MRO - позволяет Python выяснить, из какого класса-предка нужно вызывать метод, если он не обнаружен непосредственно в классе-потомке.
Если распечатаем MRO -> print(TeamLeader.__mro__)
:
Василий пишет код
(<class '__main__.TeamLeader'>, <class '__main__.Coder'>, <class '__main__.ProjectManager'>, <class '__main__.Specialist'>, <class 'object'>)
TeamLeader -> Coder -> ProjectManager -> Specialist -> object
Что выходит? Сначала ищем метод в классе TeamLeader
, затем в Coder
, затем в ProjectManager
и только потом в Specialist
. В данном случае метод work найден в классе Coder
, отсюда и результат "Василий пишет код".
Следовательно, что бы получить "Василий управляет проектом", необходимо изменить порядок наследования:
class TeamLeader(ProjectManager, Coder):
pass
Теперь MRO будет:
TeamLeader -> ProjectManager -> Coder -> Specialist -> object
Таким образом, метод work будет найден в классе ProjectManager
, и результат - "Василий управляет проектом".
Василий управляет проектом
(<class '__main__.TeamLeader'>, <class '__main__.ProjectManager'>, <class '__main__.Coder'>, <class '__main__.Specialist'>, <class 'object'>)
В качестве академического интереса. Раз TeamLeader наследует и от Coder, и от ProjectManager, то делает работу того и другого.
class Specialist:
def __init__(self, name):
self.name = name
def drink_coffee(self):
return f"{self.name} пьет кофе"
def work(self):
return f"{self.name} выполняет работу"
class Coder(Specialist):
def work(self):
return f"{self.name} пишет код"
class ProjectManager(Specialist):
def work(self):
return f"{self.name} управляет проектом"
class TeamLeader(Coder, ProjectManager):
def __getattribute__(self, attr):
match attr:
case 'work':
_str = "\n".join(
getattr(_cls, attr, lambda _: "")(self)
for _cls in self.__class__.__bases__
)
return lambda: _str
case _:
return super().__getattribute__(attr)
t = TeamLeader("Василий")
print(t.work())