python 3 : clean example for inheritance & abstract methods?

标签 python python-3.x inheritance abstract-class multiple-inheritance

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

假设我们有一个基本的 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) 我们需要访问CanJump中的self.position,因此我们必须继承Entity?!如果我们这样做,我们必须在类 CanJump 中实现抽象方法 attack()。这没有意义,因为 CanJump 应该只是赋予实体一种新型移动的能力。 (2) 将来我们可能想实现一个装饰器,在执行 move() 之前检查实体的条件是否为 Condition.DEAD attack(), ... 这也意味着 CanJump 需要访问 self.condition

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

如果需要进一步子类化怎么办?例如。我们可能有兴趣创建一个 UndeadHuman,例如 class UndeadHuman(Undead, Human)。由于线性化(Undead 按顺序排在第一位)它应该具有 Undeadattack 行为,但它还需要 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/

相关文章:

python - 如何在 Python 中打印棋盘格图案?

python - PyTorch 中卷积的输出维度

python-3.x - 匹配条件时合并多个相同长度的列表

java - 我该如何解决这个 Java 继承问题?

c++ - 如果基类被公开继承,基类的公共(public)静态函数是否成为派生类的成员函数?

python - 如何从父类(super class)中捕获的异常创建子类实例?

python - 如何给十六进制添加前导0

python - Celery 相当于一个 JoinableQueue

python - 将字符串日期转换为另一个字符串日期

python - 有没有更有效的实现方式?