我想定义一个名为"Dice"的父类和几个子类,它们代表不同类型的骰子(例如D6,D20等)。 每个骰子都有一些共同的结构,例如,您可以掷出每个骰子,但决定输出的规则是不同的。
我想定义一个在父类 Dice 中掷骰子的方法,这样我就不必为每个子类定义 roll,但它还需要引用在子类中定义的骰子类型。
这是代码片段:
import numpy
class Dice:
def __init__(self, name):
self.name = name
def roll(self):
return self.result()
class D6(Dice):
def __init__(self, name):
super().__init__(name)
def result(self):
return numpy.random.randint(1, 6)
我想知道在父方法">roll"中引用儿童方法"结果"是否被认为是糟糕的设计,或者我是否很高兴。 我知道解决我疑问的一种方法是在实例化新骰子时给出面数作为输入,但将来我想定义遵循更复杂规则的更复杂的骰子。
这通常使用抽象方法完成。 您在Dice
声明每个子类都必须实现 roll 方法。 代码如下所示:
如果您尝试调用Dice()
或者尝试创建未定义result()
的子类的实例,Python 将给出错误。
from abc import ABC, abstractmethod
class Dice(ABC):
def __init__(self, name):
self.name = name
def roll(self):
return self.result()
@abstractmethod
def result(self): ...
请注意,末尾的省略号实际上是省略号,而不是供你写东西的占位符。 按照惯例,使用...
作为抽象方法的主体是指示"这不是真正的实现"的方式。
你想要的是一个抽象的基类:
from abc import ABC, abstractmethod
from random import randint
class Die(ABC):
def __init__(self, name):
self.name = name
@abstractmethod
def roll(self) -> int:
...
class D6(Die):
def roll(self) -> int:
return randint(1, 6)
dice = [D6("1d6") for _ in range(3)]
print(sum(d6.roll() for d6 in dice)) # prints the result of rolling 3d6
由于常见的骰子类型是多面体骰子,你可以用另一个(非抽象)类来表示它,其中子类在调用super().__init__()
时提供面数:
from abc import ABC, abstractmethod
from random import randint
class Die(ABC):
def __init__(self, name: str) -> None:
self.name = name
@abstractmethod
def roll(self) -> int:
...
class PolyhedralDie(Die):
def __init__(self, name: str, faces: int) -> None:
super().__init__(name)
self.faces = faces
def roll(self) -> int:
return randint(1, self.faces)
class D6(PolyhedralDie):
def __init__(self, name) -> None:
super().__init__(name, 6)
dice = [D6("1d6") for _ in range(3)]
# could also say PolyhedralDie("1d6", 6) rather than D6("1d6")
print(sum(d6.roll() for d6 in dice))
其他假设类型的骰子(加载的骰子? 非欧几里得骰子?将直接从Die
继承,并且必须实现自己的roll
.