Python 3:继承和抽象方法的干净示例?
Python 3: clean example for inheritance & abstract methods?
实际代码大不相同,而且主题完全不同,但我觉得这个小例子可能更好,因为我的问题是理解复杂继承场景(而不是我的特定领域)的关键概念。
假设我们有一个基本的 Entity
class:
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
这是一个基础 class 其他具体实体继承自并且 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 风格)来实现 "abilities" 和其他东西。
例如,如果Orc
和Human
不仅要移动,还要能够跳跃,那么有这样的东西会很有趣:
class CanJump(Entity):
def jump(self):
self.position += 2
class Orc(Entity, CanJump):
(...)
class Human(Entity, CanJump):
(...)
这引入了两个问题。 (1) 我们需要访问 CanJump
中的 self.position
,因此我们必须继承 Entity
?!如果我们这样做,我们必须在classCanJump
中实现抽象方法attack()
。这没有意义,因为 CanJump
应该只是赋予实体一种新型移动的能力。 (2) 将来我们可能想实现一个装饰器,在执行 move()
、attack()
之前检查实体的条件是否为 Condition.DEAD
,... 这也意味着 CanJump
需要访问 self.condition
.
对于此类问题,什么是干净的解决方案?
如果需要进一步子class怎么办?例如。我们可能有兴趣创建一个像 class UndeadHuman(Undead, Human)
这样的 UndeadHuman
。由于线性化(Undead
首先),它应该具有 Undead
的 attack
行为,但它还需要 Human
的 CanJump
。
[W]e need access to self.position in CanJump, thus we have to inherit from Entity?!
不,你不知道。您可以将 CanJump
视为 混入 class,它只是增加了功能。 subclass CanJump
的任何 classes 都应具有 position
属性。 CanJump
class 本身不需要继承自 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
的子 class 实现所需的属性效果很好。
如果您想强制 class 的用户定义某些属性,您可以使用元 class。我不会重复信息,而是会向您指出 @kindall's answer,它处理这个问题相当优雅。
我很想通过组合而不是继承来对此建模。
你可以有一个用于每个生物的 Entity
class 和一个单独的 RaceModifier
class 来设置相关的 constants/adjustments 和能力。然后每个生物都可以有一个列表来模拟游戏中的种族,例如:
class RaceModifier:
def __init__(self):
...
self.abilities = []
class Orc(RaceModifier):
def __init__(self):
super().__init__()
self.extra_hit_points = 50
self.extra_damage = 2
self.abilities = [JUMPING]
class Undead(RaceModifier):
def __init__(self):
super().__init__()
self.abilities = [REGEN_ON_ATTACK]
class Entity:
...
def has_ability(self, ability): # ability = JUMPING etc
for race in self.race_modifiers:
if ability in race.abilities:
return True
for eq in self.equipment: # wearing a ring of jumping ? :)
if ability in eq.abilities:
return True
return False
实际代码大不相同,而且主题完全不同,但我觉得这个小例子可能更好,因为我的问题是理解复杂继承场景(而不是我的特定领域)的关键概念。
假设我们有一个基本的 Entity
class:
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
这是一个基础 class 其他具体实体继承自并且 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 风格)来实现 "abilities" 和其他东西。
例如,如果Orc
和Human
不仅要移动,还要能够跳跃,那么有这样的东西会很有趣:
class CanJump(Entity):
def jump(self):
self.position += 2
class Orc(Entity, CanJump):
(...)
class Human(Entity, CanJump):
(...)
这引入了两个问题。 (1) 我们需要访问 CanJump
中的 self.position
,因此我们必须继承 Entity
?!如果我们这样做,我们必须在classCanJump
中实现抽象方法attack()
。这没有意义,因为 CanJump
应该只是赋予实体一种新型移动的能力。 (2) 将来我们可能想实现一个装饰器,在执行 move()
、attack()
之前检查实体的条件是否为 Condition.DEAD
,... 这也意味着 CanJump
需要访问 self.condition
.
对于此类问题,什么是干净的解决方案?
如果需要进一步子class怎么办?例如。我们可能有兴趣创建一个像 class UndeadHuman(Undead, Human)
这样的 UndeadHuman
。由于线性化(Undead
首先),它应该具有 Undead
的 attack
行为,但它还需要 Human
的 CanJump
。
[W]e need access to self.position in CanJump, thus we have to inherit from Entity?!
不,你不知道。您可以将 CanJump
视为 混入 class,它只是增加了功能。 subclass CanJump
的任何 classes 都应具有 position
属性。 CanJump
class 本身不需要继承自 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
的子 class 实现所需的属性效果很好。
如果您想强制 class 的用户定义某些属性,您可以使用元 class。我不会重复信息,而是会向您指出 @kindall's answer,它处理这个问题相当优雅。
我很想通过组合而不是继承来对此建模。
你可以有一个用于每个生物的 Entity
class 和一个单独的 RaceModifier
class 来设置相关的 constants/adjustments 和能力。然后每个生物都可以有一个列表来模拟游戏中的种族,例如:
class RaceModifier:
def __init__(self):
...
self.abilities = []
class Orc(RaceModifier):
def __init__(self):
super().__init__()
self.extra_hit_points = 50
self.extra_damage = 2
self.abilities = [JUMPING]
class Undead(RaceModifier):
def __init__(self):
super().__init__()
self.abilities = [REGEN_ON_ATTACK]
class Entity:
...
def has_ability(self, ability): # ability = JUMPING etc
for race in self.race_modifiers:
if ability in race.abilities:
return True
for eq in self.equipment: # wearing a ring of jumping ? :)
if ability in eq.abilities:
return True
return False