通过"data defined overlays"修改基类



不确定标题是否正确,但当你不知道该怎么称呼它时,很难问一个关于它的问题。

想象一下,你有一类类似的东西

class Creature():
    def __init__( self ):
        self.str = 13
        self.dex = 10
        ...

这个生物将被赋予其他影响它的定义。在这种情况下,想象一个生物对象被赋予了兽人种族。兽人会将其str增加2。一个生物可以获得多个这样的属性修改模板。

有没有一种方法可以组织代码,使其优雅、Python化、易于维护,并且可能是数据驱动的?

首先,以下是如何在不使用数据驱动的情况下实现

class Orc(Creature):
    def __init__(self):
        super().__init__()
        self.str += 2
        self.race = 'Orc'

但是,如果您希望它是数据驱动的呢?假设数据只是存储在一个表中,该表将种族名称映射到dicts,dicts本身将属性名称映射到奖金。像这样:

races = {
    'Human': {},
    'Orc': {'str': +2, 'cha': -1},
    'Elf': {'str': -1, 'dex': +1, 'int': +1}
}

它可能存储在JSON文件、SQLite数据库或任何您想要的东西中;它不必是嵌入到源代码中的dict文字。我这样做只是为了让这个例子简单。

我们可以通过编程创建HumanOrcElf子类,但可能没有充分的理由。我们真正需要的只是一个工厂函数,它创建了一个种族实例,然后它们的行为都是一样的。因此:

def create_creature(race):
    bonuses = races.get(race, {})
    creature = Creature()
    for attr, bonus in bonuses.items():
        setattr(creature, attr, getattr(creature, attr) + bonus)
    creature.race = race
    return creature

这里唯一棘手的部分是setattr行。关键是,在编写代码时,我们不知道属性的名称,我们只在运行时知道,但我们仍然希望能够获取或设置该属性的值。通常您不想使用setattrgetattr,但当您需要按名称动态访问属性时,这正是它们的用途。

然而,值得注意的是,还有另一种选择。您的代码中有多少实际上依赖于它们作为Creature类的属性?他们可能只是一个dict的成员吗?

class Creature:
    def __init__(self):
        self.stats = {'str': 13, 
                      'dex': 10,
                      # ...
                     }

如果是这样的话,那么那条丑陋的setattr线就会变得更好:

creature.stats[attr] += bonus

另一方面,程序中的一些其他代码可能会变得更丑陋。也许不是这样:

if player.str + roll(20) > enemy.str + roll(20):

…你必须写:

if play.stats['str'] + roll(20) > enemy.stats['str'] + roll(20)

这种权衡几乎总是出现在数据驱动的对象中:程序的一部分希望将它们视为数据,另一部分则希望将其视为对象,并且必须平衡两者。

这里,Orc继承自Creature,并将其str增加2:

class Creature():
    def __init__( self ):
        self.str = 13
        self.dex = 10
class Orc(Creature):
    def __init__( self ):
        Creature.__init__( self )
        self.str += 2

这里有一个类可以统治它们:

class Creature():
    d_str = {None:13, "Orc":15}
    d_dex = {None:10, "Orc":10}
    def __init__( self, race=None ):
        self.race = race
        self.str = self.d_str[race]
        self.dex = self.d_dex[race]

最新更新