控制Python中的类的方法返回的内容

时间:2019-02-14 05:40:19

标签: python abstract-class

我的目标是创建一堆(派生的)类,这些类在调用其主要的“运行”方法时返回相同的结构化数据/变量。换句话说,当调用run方法时,它必须返回“ a,b,c,d,e,f,g”,依此类推。

为了演示,我举了一个游戏示例,其中基类为Enemy,两个可能的子类为KoboldCrow。主要的“运行”方法是do_battle,这场战斗的结果必须返回player_hp_lostgold_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_lostgold_dropped。但是,请注意,用户可以创建Enemy的新子类,而不能调用self.generate_battle_results并返回自己选择的字典,这是我要避免的。有什么办法吗?

3 个答案:

答案 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_battlegenerate_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)一样将方法设为私有。