Во-первых, вот как вы бы это сделать без с его данными:
class Orc(Creature):
def __init__(self):
super().__init__()
self.str += 2
self.race = 'Orc'
Но что, если вы хотите, чтобы данные управлялись данными? Предположим, что данные просто хранятся в таблице, которая отображает имена расы в 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
? Могли ли они быть членами дикта?
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)
Этот компромисс почти всегда приходит с объектами, управляемыми данными: одна часть вашей программы хочет, чтобы рассматривать их как данные, другая часть хочет относиться к ним как к объектам, и вы должны уравновесить их.
Класс, чтобы управлять ими, все умнее. Полагаю, я мог бы установить self.race и сделать эту работу очень хорошо. Хорошее предложение. Благодарю. – robbie
@robbie Хорошая точка. Я добавил «self.race» к образцу кода. – John1024
Вы действительно не должны называть 'Существо .__ init__' напрямую. Используйте 'super'. (Если это код Python 2.x, это также означает использование 'class Creature (object):', но вы все равно хотите сделать это изменение ...) В противном случае представьте, как бы вы определили «OrcBarbarian», «Орк» и «Варвар». – abarnert