我的目标是创建一堆(派生的)类,这些类在调用其主要的“运行”方法时返回相同的结构化数据/变量。换句话说,当调用run方法时,它必须返回“ a,b,c,d,e,f,g”,依此类推。
为了演示,我举了一个游戏示例,其中基类为Enemy
,两个可能的子类为Kobold
和Crow
。主要的“运行”方法是do_battle
,这场战斗的结果必须返回player_hp_lost
和gold_dropped
以及存储在other
中的任何其他信息。
这里是代码
import abc
from typing import Dict
class Enemy(abc.ABC):
def __init__(self, health: int, armor: int):
self.health
self.armor
def generate_battle_results(self,
player_hp_lost: int,
loot_dropped: int,
other: Dict):
battle_results = {
"player_hp_lost": player_hp_lost,
"gold_dropped": loot_dropped,
"other": other
}
@abc.abstractmethod
def do_battle(self)
class Kobold(Enemy)
def __init__(self, rage_level: int, *args, **kwargs):
self.rage_level = rage_level
super().__init__(*args, **kwargs)
def do_battle(self):
# some convoluted logic that results in player hp lost, loot dropped, etc
player_hp_lost = 5
gold_dropped = 0
other = {"time taken to finish battle": 10,
"fun had": 2
}
return self.generate_battle_results(player_hp_lost=player_hp_lost,
gold_dropped=gold_dropped,
other=other)
def Crow(Enemy)
def __init__(self, num_feathers: int, *args, **kwargs):
self.num_feathers = num_feathers
super().__init__(*args, **kwargs)
def do_battle(self):
# some convoluted logic that results in player hp lost, loot dropped, etc
player_hp_lost = 3
gold_dropped = 1
other = {"kaw kaws before death": 10,
"estimated annoyance factor":6
}
return self.generate_battle_results(player_hp_lost=player_hp_lost,
gold_dropped=gold_dropped,
other=other)
在上面的示例中,我希望do_battle
始终返回player_hp_lost
和gold_dropped
。但是,请注意,用户可以创建Enemy
的新子类,而不能调用self.generate_battle_results
并返回自己选择的字典,这是我要避免的。有什么办法吗?
答案 0 :(得分:1)
您可以定义一个具体的do_battle
来调用generate_battle_results
,然后要求用户实现一些其他方法来返回将要馈入该方法的值。
class Enemy:
...
def do_battle(self):
return self.generate_battle_results(**self.simulate_combat())
@abstractmethod
def simulate_combat(self):
raise NotImplementedError()
现在,simulate_combat
方法必须返回包含这3个值的字典,否则对generate_battle_results
的调用将抱怨缺少参数。
请注意,有足够动力的程序员使用此模块仍可以覆盖do_battle
或generate_battle_results
,并使他们做自己想做的事情。这可以。只要您的代码清晰易用,其他人以后要修改它们以适合自己的目的就可以。
答案 1 :(得分:0)
但是,请注意,用户可以创建一个
Enemy
的新子类,而不必调用self.generate_battle_results
并返回自己选择的字典,这是我要避免的。
使用您的代码的人可以做更多的事情。例如,用户可以完全不使用Enemy
类来创建自己的类。我的意思是用户可以做任何事情而不必担心您想要什么。我想对于任何编程语言都是如此,特别是如果有源代码。
有什么办法吗?
我相信在Python中这是不可能的。
答案 2 :(得分:0)
一种实现方法是提供子类可以覆盖的方法,然后将该方法包装到父类中的另一个方法中以检查错误:
+-----+-----------+------+
| 1| Ajinkya| 100|
| 2| Shital| 200|
| 3| Sameer| |
| 4| Poonam| 400|
+-----+-----------+------+
如您所见,子类仅应实现class Enemy(abc.ABC):
def __init__(self, health: int, armor: int):
self.health = health
self.armor = armor
def do_battle(self):
data = self.subclass_do_battle()
# check that all mandatory parts are present
for k in ('player_hp_lost', 'gold_dropped'):
# this may raise KeyError
_ = data[k]
return data
@abc.abstractmethod
def subclass_do_battle(self):
return {}
class Kobold(Enemy):
def __init__(self, rage_level: int, *args, **kwargs):
self.rage_level = rage_level
super().__init__(*args, **kwargs)
def subclass_do_battle(self):
# some convoluted logic that results in player hp lost, loot dropped, etc
return {
'player_hp_lost': 5,
'gold_dropped': 0,
'time taken to finish battle': 10,
'fun had': 2,
}
class Crow(Enemy):
def __init__(self, num_feathers: int, *args, **kwargs):
self.num_feathers = num_feathers
super().__init__(*args, **kwargs)
def subclass_do_battle(self):
# some convoluted logic that results in player hp lost, loot dropped, etc
return {
'player_hp_lost': 5,
'gold_dropped': 0,
'kaw kaws before death': 9,
'estimated annoyance factor': 6,
}
,父类Enemy.subclass_do_battle()
将调用实现的子类方法并检查返回数据,然后将其返回给最终调用者。
并且无法避免任何人重写方法Enemy.do_battle()
。在Enemy.do_battle()
中,我们通常以所有人都同意为成年人的借口工作;此语言的设计目的不是像其他语言(例如Python
)一样将方法设为私有。