实际代码大不相同,而且是完全不同的主题,但我觉得这个小例子可能会更好,因为我的问题是理解复杂继承场景(而不是我的特定领域)的关键概念。
假设我们有一个基本的 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)来实现“能力”和其他东西。
例如,如果
Orc
和 Human
不仅应该移动,还应该能够跳跃,那么有这样的东西会很有趣: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.DEAD
, move()
,...之前检查实体的条件是否为 attack()
……这也意味着 CanJump
需要访问 self.condition
。对于此类问题,什么是干净的解决方案?
如果需要进一步的子类化怎么办?例如。我们可能有兴趣创建一个
UndeadHuman
像 class UndeadHuman(Undead, Human)
。由于线性化(首先是 Undead
),它应该具有 attack
的 Undead
行为,但它也需要来自 CanJump
的 Human
。 最佳答案
不,你没有。您可以将 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/