实际代码大不相同,而且是完全不同的主题,但我觉得这个小例子可能会更好,因为我的问题是理解复杂继承场景(而不是我的特定领域)的关键概念。

假设我们有一个基本的 Entity 类:

from enum import Enum
from abc import abstractmethod

class Condition(Enum):
    ALIVE = 1
    DEAD = 2
    UNDEAD = 3

class Entity(object):

    def __init__(self):
        self.condition = Condition.ALIVE
        self.position = 0
        self.hitpoints = 100

    def move(self):
        self.position += 1

    def changeHitpoints(self, amount):
        self.hitpoints += amount

    @abstractmethod
    def attack(self, otherEntity):
        pass

这是其他具体实体继承的基类,attack() 必须是抽象的,因为每个实体都应该实现自己的攻击方法风格。

现在我们可以实现一些实体:
class Orc(Entity):

    def __init__(self):
        super().__init__()
        self.hitpoints = 150
        self.damage = 10

    def attack(self, otherEntity : Entity):
        otherEntity.changeHitpoints(-self.damage)

class Human(Entity):

    def __init__(self):
        super().__init__()
        self.damage = 8

    def attack(self, otherEntity : Entity):
        otherEntity.changeHitpoints(-self.damage)

class Undead(Entity):

    def __init__(self):
        super().__init__()
        self.condition = Condition.UNDEAD
        self.damage = 5

    def attack(self, otherEntity : Entity):
        # harm enemy
        otherEntity.changeHitpoints(-self.damage)
        # heal yourself
        self.changeHitpoints(1)

这工作正常。但是,我正在努力找出一个很好的解决方案( DRY -style)来实现“能力”和其他东西。

例如,如果 OrcHuman 不仅应该移动,还应该能够跳跃,那么有这样的东西会很有趣:
class CanJump(Entity):

    def jump(self):
        self.position += 2

class Orc(Entity, CanJump):
    (...)

class Human(Entity, CanJump):
    (...)

这引入了两个问题。 (1) 我们需要在 self.position 中访问 CanJump ,因此我们必须从 Entity 继承?!如果我们这样做,我们必须在类 attack() 中实现抽象方法 CanJump 。这是没有意义的,因为 CanJump 应该只是赋予实体一种新型运动的能力。 (2) 将来我们可能想要实现例如一个装饰器,在执行 Condition.DEADmove() ,...之前检查实体的条件是否为 attack() ……这也意味着 CanJump 需要访问 self.condition

对于此类问题,什么是干净的解决方案?

如果需要进一步的子类化怎么办?例如。我们可能有兴趣创建一个 UndeadHumanclass UndeadHuman(Undead, Human) 。由于线性化(首先是 Undead),它应该具有 attackUndead 行为,但它也需要来自 CanJumpHuman

最佳答案



不,你没有。您可以将 CanJump 视为一个混合类,它只是添加了功能。任何子类 CanJump 的类都应该具有 position 属性。而 CanJump 类本身不需要从 Entity 继承。这样做:

class CanJump:
    def jump(self):
        self.position += 2

会很好。然后你可以这样做:
class Orc(Entity, CanJump):
    (...)

class Human(Entity, CanJump):
    (...)

这是一个完整的示例,演示了上述操作:
from abc import abstractmethod


class A:
    def __init__(self):
        self.a = 0

    @abstractmethod
    def m(self):
        pass


class C:
    def c(self):
        self.a += 1


class B(A, C):
    def __init__(self):
        super().__init__()

    def m(self):
        print('method in B')


b = B()
print(b.a) # 0
b.c()
print(b.a) # 1
b.m() # method in B

您看,您可以在方法实现中使用当前不存在的属性。属性只需要在调用方法时存在。让 CanJump 的子类实现所需的属性效果很好。

如果您想强制您的类的用户定义某些属性,您可以使用元类。我不会重复信息,而是将您指向 @kindall's answer ,它相当优雅地处理这个问题。

关于Python 3 : clean example for inheritance & abstract methods?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46101833/

10-09 20:41