【问题标题】:Why can't I have a varargs constructor and another constructor with fixed arguments?为什么我不能有一个可变参数构造函数和另一个具有固定参数的构造函数?
【发布时间】:2011-01-29 20:21:24
【问题描述】:

这是我目前所拥有的:

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' object has no attribute 'roll'。这意味着它首先进入可变参数构造函数。我可以在这里做些什么来完成这项工作,或者还有其他选择吗?

【问题讨论】:

  • 它不会“首先进入可变参数构造函数”。在类定义中,所有方法都是唯一的,那里没有两个构造函数(它们实际上是初始化器)。你的die_list 是一个通过的整数元组。

标签: python constructor


【解决方案1】:

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

Python doesn't support method overloading,所以你手头至少有两个选择。

  • 仅定义可变参数构造函数。检查参数列表的长度和前几个元素的类型以确定要运行的逻辑。实际上,您可以将两个构造函数合并为一个。
  • 将其中一个构造函数转换为静态工厂方法。例如,您可以删除第一个构造函数,保留一个可变参数,然后定义一个新的工厂方法。

我更喜欢第二种方法,它可以让你清晰地分离你的逻辑。您还可以为您的工厂方法选择一个更具描述性的名称; from_n_sided_diceDice 提供更多信息:

@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)]

编辑:您可以使用函数装饰器emulate method overloading(通过动态分派,而不是您可能习惯的静态分派,但 Python 中不存在静态类型)。您可能必须设计自己的解决方案来支持 *args**kwargs,并且使用更精确名称的单独方法通常仍然是更好的解决方案。

【讨论】:

  • OP 代码中的所有Die 对象都是无状态的,因此相同Die 的N 个副本应该产生与N 个不同Die 对象相同的结果。
  • 现在,当然。这似乎是一个潜在的陷阱,如果可以引用同一个 Die,我会使用类似单例的 API 来表达我的意图;例如,[Die.with_sides(sides)] * num_dice.
【解决方案2】:

您想要的是一个单一的__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)

【讨论】:

  • Yohr 当前的构造函数有时使self.die_list 成为一个元组,有时则成为一个列表。我会尽量让它每次都一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-04
  • 2015-09-22
  • 1970-01-01
  • 2015-02-10
  • 1970-01-01
  • 1970-01-01
  • 2019-01-18
相关资源
最近更新 更多