Борьба с наследованием классов & super () .__ init__ - PullRequest
0 голосов
/ 02 июля 2018

Очень плохо знаком с Python, пытаясь создать игру, в которой можно создать любое количество армий, но каждая армия будет предварительно отображать имена солдат.

Я думаю, что мне нужно использовать super init, чтобы действительно сократить дублирующийся код, но я не могу понять, как заставить его работать. Из того, что я понимаю, мой класс Army должен быть родительским классом, с RedArmy и Scout в качестве подклассов. Я просто изо всех сил пытаюсь понять, где super().__init__() должен войти?

class Army:    
    def __init__(self):
        self.color = None
        self.scoutname = None
        self.demomanname = None
        self.medicname = None

    def train_scout(self, weapon):
        return Scout(self.color, self.scoutname, weapon)

class RedArmy(Army):

    def __init__(self):
        self.color = "Red"
        self.scoutname = "Yankee"
        self.demomanname = "Irish"
        self.medicname = "Dutch"

class BlueArmy(Army):
    pass

class Scout:
    specialization = "fast captures"

    def __init__(self, color, scoutname, weapon):
        self.color = color
        self.scoutname = scoutname
        self.weapon = weapon

    def introduce(self):
        return (f'Hi I\'m {self.scoutname}, I do {self.specialization} and I wield a {self.weapon}')


my_army = RedArmy()    
soldier_1 = my_army.train_scout("baseball bat")
print(soldier_1.introduce())

Ответы [ 2 ]

0 голосов
/ 03 июля 2018

Передача значений в качестве аргументов имеет смысл, если вы хотите, чтобы ваши классы использовали super и init:

class Army:    
    def __init__(self, color=None, scoutname=None, demomanname=None,
                 medicname=None):
        self.color = color
        self.scoutname = scoutname
        self.demomanname = demomanname
        self.medicname = medicname

    def train_scout(self, weapon):
        return Scout(self.color, self.scoutname, weapon)


class RedArmy(Army):
    def __init__(self, color="Red", scoutname="Yankee", demomanname="Irish",
                 medicname="Dutch"):
        super().__init__(color, scoutname, demomanname, medicname)


class Scout:
    specialization = "fast captures"

    def __init__(self, color, scoutname, weapon):
        self.color = color
        self.scoutname = scoutname
        self.weapon = weapon

    def introduce(self):
        return (
            f'Hi I\'m {self.scoutname}, I do {self.specialization} and I wield '
            f'a {self.weapon}')


my_army = RedArmy()    
soldier_1 = my_army.train_scout("baseball bat")
print(soldier_1.introduce())
0 голосов
/ 02 июля 2018

Где поставить super().__init__ (если где-либо) зависит от специфики вашей иерархии классов.

Чаще всего, если это имеет значение, куда вы положили, вы хотите это в самом начале метода __init__ подкласса. Это гарантирует, что все переменные базового класса установлены, все его инварианты выполнены, и все его методы могут быть вызваны остальной частью кода подкласса __init__.

В вашем случае это важно, потому что вы устанавливаете одинаковые атрибуты в базовом классе и подклассе. Вы, очевидно, хотите, чтобы версия подкласса была той, которая принимает, поэтому она должна идти после заданий по умолчанию, а не до:

class RedArmy(Army):
    def __init__(self):
        super().__init__()
        self.color = "Red"
        self.scoutname = "Yankee"
        self.demomanname = "Irish"
        self.medicname = "Dutch"

Однако стоит подумать, действительно ли вы хотите, чтобы базовый класс установил эти переменные на None.

Я предполагаю, что в вашем реальном коде BlueArmy не просто перейдет к pass, но вместо этого сделает то же самое, что и RedArmy, заменив все эти значения некоторыми строками.

Кроме того, остальная часть вашего кода предположительно будет предполагать, что там есть допустимые строки, а не None. Исключение типа TypeError: '<' not supported between instances of 'NoneType' and 'str' сложнее отладить, чем AttributeError: 'GreenArmy' object has no attribute 'scoutname', не проще, так почему бы просто не оставить настройки по умолчанию? Тогда вы можете полностью исключить Army.__init__ и вам не нужно беспокоиться о вызове super в инициализаторах подкласса.

Или, альтернативно, вы можете Army.__init__ принять параметры, которые используются для присвоения значений, и вызвать подклассы super().__init__("Red", "Yankee", "Irish", "Dutch"). Затем Army() вызовет TypeError вместо создания недопустимого экземпляра Army. Или вы можете сделать @abstractmethod с именем self._setup(), который Army.__init__ вызывает и ожидает от каждого подкласса, так что Army() поднимет еще более значимый TypeError в отношении создания абстрактного класса. Эти усовершенствования облегчают отладку ваших Army подклассов - если у вас есть только два из них, они могут быть просто пустой тратой времени, но если у вас есть куча из них, которые будут разработаны различными людьми или старше длительный период времени, это может стоить того.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...