实际的代码有很大的不同,而且主题完全不同,但我觉得这个小例子可能更好,因为我的问题是理解复杂继承场景(而不是我的特定领域)的关键概念。
假设我们有一个基本的 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) 我们需要访问CanJump
中的self.position
,因此我们必须继承Entity
?!如果我们这样做,我们必须在类 CanJump
中实现抽象方法 attack()
。这没有意义,因为 CanJump
应该只是赋予实体一种新型移动的能力。 (2) 将来我们可能想实现一个装饰器,在执行 move()
之前检查实体的条件是否为 Condition.DEAD
, attack()
, ... 这也意味着 CanJump
需要访问 self.condition
。
对于此类问题,什么是干净的解决方案?
如果需要进一步子类化怎么办?例如。我们可能有兴趣创建一个 UndeadHuman
,例如 class UndeadHuman(Undead, Human)
。由于线性化(Undead
按顺序排在第一位)它应该具有 Undead
的attack
行为,但它还需要 CanJump
来自人类
。
最佳答案
[W]e need access to self.position in CanJump, thus we have to inherit from Entity?!
不,你不知道。您可以将 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/