为什么我不能同时拥有一个可变参数构造函数和一个固定参数构造函数?

1 投票
2 回答
601 浏览
提问于 2025-04-16 10:51

这是我目前的进展:

class Die (object):
    def __init__(self,sides):
        self.sides = sides

    def roll(self):
        return random.randint(1,self.sides)

    def __add__(self,other):
        return Dice(self,other)

    def __unicode__(self):
        return "1d%d" % (self.sides)

    def __str__(self):
        return unicode(self).encode('utf-8')

class Dice (object):
    def __init__(self, num_dice, sides):
        self.die_list = [Die(sides)]*num_dice

    def __init__(self, *dice):
        self.die_list = dice

    def roll(self):
        return reduce(lambda x, y: x.roll() + y.roll(), self.die_list)

但是当我尝试使用 Dice(3,6) 并接着调用 roll 这个动作时,它提示我说不能这样做,因为 'int' 对象没有 'roll' 这个属性。这意味着它首先进入了可变参数的构造函数。我该怎么做才能让它正常工作,或者有没有其他的替代方案?

2 个回答

1

你需要的是一个简单的 __init__ 方法,应该按照下面的方式来定义:

class Dice (object):
    def __init__(self, *args):
        if not isinstance(args[0], Die):
            self.die_list = [Die(args[0]) for _ in range(args[1])]
        else:
            self.die_list = args
    def roll(self):
        return sum(x.roll() for x in self.die_list)
4

正如你在问题中观察到的,varargs构造函数正在被调用。这是因为第二个定义的 Dice.__init__ 是在覆盖第一个,而不是重载它。

Python不支持方法重载,所以你至少有两种选择。

  • 只定义一个varargs构造函数。检查参数列表的长度和前几个元素的类型,以确定要执行的逻辑。实际上,你就是把两个构造函数合并成一个。
  • 把其中一个构造函数转换成静态工厂方法。例如,你可以删除第一个构造函数,保留varargs的那个,然后定义一个新的工厂方法。

我更喜欢第二种方法,这样可以更清晰地分离你的逻辑。你还可以为你的工厂方法选择一个更具描述性的名字; from_n_sided_dice 比单纯的 Dice 更有信息量:

@staticmethod
def from_n_sided_dice(num_dice, sides):
    return Dice([Die(sides)] * num_dice)

顺便说一下:这真的是你想要的吗? [Die(sides)] * num_dice 会返回一个包含多个对同一个Die对象的引用的列表。相反,你可能想要 [Die(sides) for _ in range(num_dice)]

编辑:你可以通过函数装饰器来 模拟方法重载(通过动态调度,而不是你可能习惯的静态调度,因为Python中并不存在静态类型)。你可能需要自己设计解决方案来支持 *args**kwargs,而且拥有更精确名称的单独方法通常仍然是更好的解决方案。

撰写回答