Наследование атрибутов класса без объявления атрибутов или лучше OOP - PullRequest
1 голос
/ 13 января 2020

Я пытаюсь создать классы, чтобы сыграть в игру MTG (карточная игра). У меня есть три соответствующих класса: MTGGame (...), MTGCard (...) и AbilityList (). Объект MTGGame имеет несколько атрибутов об игроке (терн, мана, ..., колода).

У игрока должна быть колода карт для игры, поэтому я создаю список объектов MTGCard для каждого игрока, являющегося колодой, и создаю объект MTGGame для каждого из соответствующих колод. Карты имеют способности, и при создании карт я сохраняю способности как функции / параметры в каждой MTGCard. Однако , мне нужны способности наследовать и получать доступ к методам / атрибутам из MTGGame и обновлять их, но если я использую super().__init__, тогда мне нужно будет вызывать свою колоду в качестве параметра для AbilityList при создании MTGCards, чего бы я еще не имел.

Можно ли этого достичь? Если нет, какие-либо предложения по улучшению моего OOP logi c для решения этой задачи?

Я знаю, что могу сделать что-то вроде этого:

class MTGGame():

    def __init__(self, deck, turn = 0, mana = 0, lifeTotal = 20, cavalcadeCount = 0, hand = [], board = []):   
        self.turn = turn
        self.mana = mana
        self.lifeTotal = lifeTotal
        ...
    def gainLife(self, lifeGained):
        self.lifeTotal = self.lifeTotal +lifeGained
    def combatPhase(self):
        for card in self.board:
            card.attackingAbility()

class MTGCard():

    def __init__(self, name, CMC, cardType, power, toughness, castedAbility, attackingAbility, activatedAbility, canAttack = False):
        ....
        self.attackingAbility = attackingAbility

Class abilityList():

    def healersHawkAbility(self, lifeAmt):
        MTGGame.gainLife(lifeAmt)

Но это будет влиять на все экземпляры MTGGame, а не на конкретный объект c MTGGame, из которого он был бы вызван. Я бы хотел, чтобы он просто обновил указанный объект c. Я хотел бы сделать что-то вроде этого, но я не знаю, как методыabilityList могли бы получить доступ к атрибутам / методам MTGGame (у объекта 'AbilityList' нет атрибута 'gainLife'):

Class abilityList():

    def healersHawkAbility(self, lifeAmt):
        #How do I access methods/attributes in MTGGame from here? self?
        self.gainLife(lifeAmt)

aL = abilityList()
#One example card:
card1 = MTGCard("Healers Hawk",1,'Creature',1,1, aL.nullAbility(), aL.healersHawkAbility, aL.nullAbility())
whiteDeck = [list of constructed MTGCard() objects, card1, card2,...,cardLast]
player1 = MTGGame(whiteDeck)
...

#Call the ability in a method contained in MTGGame:
player1.combatPhase()
#Would call something like this inside
card.attackingAbility()

#Which may refer to card.healersHawkAbility() since we stored healersHawkAbility() as an attribute for that MTGCard, 
#and would declare gainLife(), which refers to self.lifeTotal or player1.lifeTotal in this case.

1 Ответ

2 голосов
/ 13 января 2020

Это отличное начало, и, очевидно, вы уже много об этом думали. Однако вы не продумали отношения между классами.

Первое, на что нужно обратить внимание: MTGGame.gainLife(lifeAmt) - это вызов метода, доступ к которому осуществляется через класс, а не через экземпляр. Это означает, что параметр self на самом деле не заполнен, т.е. вы получите ошибку, потому что ваш метод ожидает 2 аргумента, но получает только один. Возможно, вы хотели сделать следующее:

class MTGGame:

    lifeTotal = 20 # Notice this is declared as a class variable

    def __init__(self, ...):
        ...

    @classmethod
    def healersHawkAbility(cls, lifeGained):
        cls.lifeTotal = cls.lifeTotal + lifeGained

Однако для этого нужны переменные класса, которые здесь лишают смысла иметь экземпляр.

Ваше наименование в программе должно предполагать, что вы занятия немного выходные Например player1 = MTGGame(). Является ли игрок игрой? Нет, конечно нет. Поэтому на самом деле вы можете захотеть переименовать свой класс MTGGame в Player, чтобы было ясно, что он относится к игроку, а не к игре. Вероятно, потребуется создать отдельный класс, называемый MTGGame, чтобы управлять взаимодействиями между игроками, например, чьим ходом, стеком, в котором находятся карты во время разрешения.

Основная задача вашего вопроса: как справиться с карты, обращающиеся к объекту игры / игрока.

Карты должны иметь возможность доступа к экземплярам класса игрока и игры, и если у игрока есть атрибут is_playing, карта не должна иметь его. Эмпирическое правило для наследования - «это». Так как карта не является игроком, она не должна наследоваться от нее или MTGGame. Вместо этого карта должна выглядеть следующим образом:

game = RevisedMTGGame()
player1 = Player()
player2 = Player()

class Card:
    def __init__(self, name, text, cost):
        self.name = name
        self.text = text
        self.cost = cost
        self.owner = None
        self.game = None

class Creature(Card):
    def __init__(self, name, text, cost, power, toughness):
        super().__init__(self, name, text, cost)
        self.power = power
        self.toughness = toughness
    def lifelink(self):
        self.owner.heal(self.power) # NOTE: this is NOT how I would implement lifelink, it is an example of how to access the owner

healersHawk = Creature("Healer's Hawk", "Flying, Lifelink", 1, 1, 1)
healersHawk.game = game
healersHawk.owner = player1

Из этого неполного примера вы можете увидеть, как вы можете легко настроить свои карты, даже со сложной механикой, и поскольку базовые классы были определены, вы можете избегать повторения кода. Возможно, вы захотите взглянуть на модель событий, чтобы в качестве примера реализовать механизм поддержки жизненного цикла c. sh удачи в продолжении игры!

...