不确定标题是否正确,但当你不知道该怎么称呼它时,很难问一个关于它的问题。
想象一下,你有一类类似的东西
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
文字。我这样做只是为了让这个例子简单。
我们可以通过编程创建Human
、Orc
和Elf
子类,但可能没有充分的理由。我们真正需要的只是一个工厂函数,它创建了一个种族实例,然后它们的行为都是一样的。因此:
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
行。关键是,在编写代码时,我们不知道属性的名称,我们只在运行时知道,但我们仍然希望能够获取或设置该属性的值。通常您不想使用setattr
和getattr
,但当您需要按名称动态访问属性时,这正是它们的用途。
然而,值得注意的是,还有另一种选择。您的代码中有多少实际上依赖于它们作为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]