Вопрос или проблема
Хотя я понимаю, что это странно, я все же хочу знать, может ли это работать, в основном для улучшения понимания. В основном это наследование класса, но также от родительского класса требуется вызывать методы в подклассе.
Пример проблемы выглядит следующим образом:
class Match:
player = Player(...)
# другие вещи здесь
class Player:
def __init__(self, name, etc)
....
self.display = Display(self)
class Display(Player):
def __init__(self, player):
super().__init__(player.name,...)
Цель этого — вызвать self.player.display.method()
из Match
, чтобы помочь отделить функциональность отображения класса Player
от всех остальных методов.
На данный момент способ, которым это сделано выше, приводит к рекурсии между Player
и Display
. Это можно просто исправить, удалив класс Display
и объединив методы в Player
. Но я не хочу этого делать. Другой вариант — найти способ остановить super().__init__
в Display
до того, как он дойдет до self.display
, возможно, с помощью флажка, но у меня это не получилось.
В моем решении я хотел бы:
Display
имел доступ ко всем данным экземпляра и методам вPlayer
, так чтоself.name
работал бы вDisplay
, например, без необходимости вызыватьself.player.name
или вручную присваиватьself.name
.Player
вызывал методы вDisplay
. Эффективно создавая общий класс между двумя, чисто для эстетических причин.
Я попытался просмотреть документацию по super() и статьи, такие как https://rhettinger.wordpress.com/2011/05/26/super-considered-super/
Использовал защитные условия, такие как if not from_super: self.display = ...
. Подозреваю, что можно использовать hasattr
, чтобы обнаружить вызов super, но у меня это не сработало.
Display, которому нужно “90% переменных” игрока, не меняет того факта, что Display — это не Player. Храните экземпляр игрока как атрибут Display, если это имеет смысл, и если проблема в наборе self.name
вместо self.player.name
, просто присвойте self.player
локальной переменной в начале ваших методов отображения – player = self.player
– и затем воспользуйтесь player.name
.
Кроме того, когда действительно есть необходимость в наследовании, способ, которым суперкласс может вызвать метод в подклассах, — это просто вызвать их; переопределенный метод в подклассе будет использоваться. Можно создать заглушки для этих методов или сделать абстрактный базовый класс — или не делать этого вовсе и просто документировать необходимые методы в подклассах.
Например, вы можете иметь class UserControledPlayer(Player):
и class BotPlayer(Player):
в этом проекте, каждая из которых будет иметь свой собственный метод def move(self, ...):
.
Ответ или решение
Наследование классов и совместное использование методов: глубокий анализ
Введение
Проектирование классов в объектно-ориентированном программировании (ООП) всегда требует тщательного планирования, особенно когда речь идет о наследовании. В данной статье мы рассмотрим сложный случай, связанный с наследованием, в рамках которого требуется создание класса Display
, использующего функционал класса Player
, избегая прямой рекурсии и сохраняя при этом логическую целостность архитектуры.
Проблема
Изучая структуру вашего кода, можно выявить основные проблемы:
- Рекурсивный вызов инициализации: При создании экземпляра класса
Display
он пытается инициализировать классPlayer
, что вызывает вторичную инициализациюDisplay
и приводит к бесконечной рекурсии. - Доступ к атрибутам класса: Желание использовать атрибуты
Player
вDisplay
, не ссылаясь на объектPlayer
явно. - Поддержание логической структуры: Необходимо сохранить разделение ответственности и чистоту архитектуры.
Предлагаемое решение
Решение данной проблемы может быть организовано следующим образом:
Структура классов
class Player:
def __init__(self, name, *args, **kwargs):
self.name = name
# Другие атрибуты игрока
self.display = Display(self)
class Display:
def __init__(self, player):
self.player = player
def show(self):
# Пример метода, который использует атрибуты Player
print(f"Игрок: {self.player.name}")
class Match:
def __init__(self):
self.player = Player("Игрок 1")
def display_player_info(self):
self.player.display.show()
Объяснение кода
-
Класс
Player
: Теперь классPlayer
инициализирует классDisplay
, передавая ссылку на себя. Это позволяетDisplay
иметь доступ ко всем атрибутамPlayer
черезself.player
. -
Класс
Display
: Метод__init__
классаDisplay
принимает экземплярPlayer
и сохраняет его. Теперь любой метод вDisplay
, напримерshow
, может получить доступ к имени игрока черезself.player.name
. -
Класс
Match
: Этот класс создаёт экземплярPlayer
и может вызывать методы отображения информации о игроке черезself.player.display.show()
. Это разделяет логику отображения и саму игровую логику.
Преимущества предложенного подхода
- Устранение рекурсии: Использование ссылки на объект
Player
позволяет избежать проблемы бесконечной рекурсии. - Чистота архитектуры: Каждый класс выполняет свою роль, не смешивая функциональность.
Player
отвечает за игровые данные, аDisplay
— за визуализацию. - Простота доступа к данным: Метод
show
в классеDisplay
легко может ссылаться на любое свойствоPlayer
, поскольку у него есть к нему доступ.
Заключение
Создание иерархии классов с использованием наследования может стать сложной задачей, особенно когда необходимо предоставить доступ ко многим атрибутам без создания циклических зависимостей. Используя предложенные решения, вы сможете сохранить чистоту и функциональность вашего кода, а также добиться нужной вам гибкости.
Данная архитектура отвечает принципам SOLID, а именно принципу единственной ответственности и принципу зависимости от абстракций, что делает ваш код более модульным и легким для сопровождения.