【问题标题】:When does a class warrant a sub-class?一个类什么时候需要一个子类?
【发布时间】:2016-08-09 23:49:14
【问题描述】:

例如,我正在尝试为一个简单的游戏构建一个敌人类。生成的每个 enemy 都有一个 type 影响它的统计信息,这些统计信息是 enemy 类中的字段。

class Enemy:
    #Base Stats for all enemies
    name = "foo"
    current_health = 4
    max_health = 4
    attack = 0
    defense = 0
    armor = 0
    initiative = 0
    initMod = 0
    alive = True

每个 type 应该是 enemy子类,像这样..

class goblin(Enemy):
    name = goblin;
    current_health = 8
    max_health = 8
    attack = 3
    defense = 2
    armor = 0

    def attack(){
        //goblin-specific attack
    }

但是这种方法意味着我必须为每个单独的 type 构建一个类(这将是 80 多个类),或者有更好的方法吗?这个 enemy 将是随机的,所以我认为 types 也可以放入使用 名称的 dictionary >types 作为关键字。虽然我不完全确定如何实现它。

【问题讨论】:

  • 有了这么多潜在的子类,您可能无法很好地建模问题。一方面,听起来您将“is-a”与“has-a”关系混淆了——可能还有类和类的实例。列出的大多数属性可能是在类或子类的构造函数中初始化的 (__init__())。此外,类方法的所有定义都会自动接收通常称为self 的第一个参数,它是类的实例。所以你需要有def attack(self):。此外,Python 不会以这种方式使用 {} 括号。

标签: python dictionary inheritance pygame python-object


【解决方案1】:

如果你想走字典路线,你可以用键返回一个元组来做这样的事情:

enemy_types = {"goblin": ("goblin", 8, 8, 3, 2, 0, goblin_attack)}

def goblin_attack(enemy):
    do_stuff()

虽然你可能想使用命名元组

https://stackoverflow.com/a/13700868/2489837

以确保您不会混淆元组中的字段

【讨论】:

  • 我认为这是我要坚持的路线。需要澄清的是,每个敌人都遵循相同的战斗准则。他们都可以攻击或防御,但他们在攻击期间所做的事情可能会根据他们的特殊能力而有所不同。我需要为字典中每种不同类型的敌人创建一个单独的函数吗?这些函数会驻留在我的敌人类文件中吗?
  • 这取决于你。我的解决方案就是为任何函数返回一个函数指针。根据他们的特殊攻击是什么(即,您是否可以只传递一个参数来说明它造成了多少伤害,或者它是否有更多与之相关的特殊内容),您可能需要为每个攻击提供一个独特的功能。无论哪种方式,您仍然可以采用此字典路由并返回元组/对象或您拥有的东西。
【解决方案2】:

如果每种敌人类型的属性集和可能的动作在不同类型中大多是通用的,我认为没有理由做任何比为该类型定义 80 项枚举并使用一个名为 @ 的类更复杂的事情987654321@.

如果您确实需要各种操作等,也许您可​​以将您的 80 种类型分组到各种集合中,然后这些集合将具有共同的属性(例如FlyingEnemyUndeadEnemy 等)。然后这些将成为您的子类。

【讨论】:

  • 我真的很喜欢这个想法,它是否也适用于 user2489837 的想法,即为不同的敌人使用字典?这样我就可以获得 FlyingEnemies 或 UndeadEnemies 的列表。
【解决方案3】:

地精应该是 Enemy 的一个实例。但是,在您的示例中,您添加了一个名为 attack 的方法。我想说最好的方法是这样的:

class Enemy:
   #Base Stats for all enemies
  def __init__ (self, **kwargs):
   self.name = "foo"
   self.current_health = 4
   self.max_health = 4
   self.attack = 0
   self.defense = 0
   self.armor = 0
   self.initiative = 0
   self.initMod = 0
   self.alive = True
   # Use this to specify any other parameters you may need
   for key in kwargs:
       exec ("self.%s = %s" % (key, kwargs[key])

现在您可以创建一个名为 goblin 的 Enemy 实例。

要回答您最初关于何时使用子类的问题,我想说使用子类很棒,但在您的情况下可能会使事情变得过于复杂。由于您的子类中只有一个方法,您可以轻松地创建一个模块,然后在 __init__ 中有一个参数来指定 attack 函数。这将进一步简化您的代码。

注意:如果您要使用子类,请确保在子类的 __init__ 中使用 super() 调用基类的 __init__

【讨论】:

  • 最后的整个for 循环可以替换为self.__dict__.update(kwargs)。通常,人们希望尽可能避免使用exec,而且通常是这样。您的缩进也不一致(而不是 4 个空格,请参阅 PEP 8 - Style Guide for Python Code)。
  • 对于我缺乏字典知识,我深表歉意,但我想我明白你在这里做什么。所以我会定义一个包含所有不同敌人类型的字典。当我创建一个新的敌人实例时,我需要输入其中一个敌人的密钥。该键用于引用字典中的类型,它将敌人字段初始化为字典中指定的值。 p.s.感谢您抽出宝贵时间回答
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-29
  • 1970-01-01
  • 2011-09-20
  • 1970-01-01
  • 2011-01-15
  • 1970-01-01
相关资源
最近更新 更多