Как поменять класс объекта на его сабкласс сохраняя все атрибуты и инстанции?

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

Код снизу это представление иерархии компании, класс работников (Worker), класс начальников (Boss) и класс корпорации(Corporation). У Boss и Worker все атрибуты кроме одного идентичные. Как поменять работника на начальника (и наоборот) сохраняя все эти атрибуты и место в иерархии. Подскажите как именно инцициализировать смену класса в методе promote_or_demote(), так чтобы все сохранилось и внутри иерархии произошли изменения?

class Worker:
    """ self.subworkers это список прямых подчиненных работников. """
    id: int
    position: str
    grade: int
    subworkers: List[Worker]
    
    def __init__(self, id: int, workplace: str, age: int, position: str, grade: int) -> None:
        self.id = id
        self.workplace = workplace
        self.age = age
        self.position = position
        self.grade = grade
        self.subworkers = []

class Boss(Worker):
     """ Класс наследует все инстанции Worker."""
     _company: str

     def __init__(self, id: int, workplace: str, age: int, position: str, grade: int, 
                                                                        company: str) -> None:
        Worker.__init__(self, id, workplace, age, position, grade)
        self._company = company

class Corporation:
      """Класс корпорации"""
      _leader: Optional[Worker]

     def __init__(self, leader: Optional[Worker] = None) -> None:
          self._leader = leader

     def promote_or_demote(self, id: int, company_name: Optional[str] = None) -> Worker:
         """ Поменяйте тип рабочего с этим <id>.
         Повысьте Worker до Boss или понизьте Boss до Worker. Все инстанции должны сохранится.
         Верните нового работника/босса.
         Условие: Если <id> это id Boss'а, то company_name должно быть None """

Ответы (2 шт):

Автор решения: Крест Доминика Туретта

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

class A:
    def print(self):
        print(self.__class__)

    def change_class(self):
        self.__class__ = B

class B(A):
    pass

obj = A()
obj.print()
obj.change_class()
obj.print()

Вывод:

<class '__main__.A'>

<class '__main__.B'>
→ Ссылка
Автор решения: Sergey Derevianko
class Worker:
    def __init__(self, id_: int, workplace: str, age: int, position: str, grade: int) -> None:
        self.id_ = id_
        self.workplace = workplace
        self.age = age
        self.position = position
        self.grade = grade
        self.subworkers = []

    def promote(self, comp):
        return Boss(self.id_, self.workplace, self.age, self.position, self.grade, comp)


class Boss(Worker):
    def __init__(self, id_: int, workplace: str, age: int, position: str, grade: int, company: str):
        super().__init__(id_, workplace, age, position, grade)
        self._company = company

    def demote(self):
        return Worker(self.id_, self.workplace, self.age, self.position, self.grade)

anyone = ... # Boss or Worker
anyone = anyone.demote() if isinstance(anyone , Boss) else anyone .promote(company_name)

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

Советы:

  1. id - зарезервированое слово в Python, используйте другое имя для атрибута
  2. Почитайте о super(), для вызова родительских функций используйте его.
  3. Не вызывайте магические методы напрямую(Worker.init), для них есть реализация правильная
  4. Все атрибуты для будущих обьектов обьявляются только внутри init, вне init вы обьявляете атрибуты класса, почитайте об этом, также посмотрите на встроену либу dataclasses
  5. Если метод или функция ничего не возвращает можете не писать для неё тайпинг.
→ Ссылка