Почему у меня не может быть конструктора varargs и другого конструктора с фиксированными аргументами?

1

Вот что я до сих пор:

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'. Это означает, что он сначала входит в конструктор varargs. Что я могу сделать здесь, чтобы сделать эту работу, или есть другая альтернатива?

  • 0
    он не «сначала входит в конструктор varargs». В определении класса все методы уникальны, там нет двух конструкторов (они на самом деле инициализаторы). Ваш die_list - это набор целых чисел, которые проходят.
Теги:
constructor

2 ответа

4
Лучший ответ

Как вы заметили в своем вопросе, вызывается конструктор varargs. Это связано с тем, что второе определение Dice.__init__ является переопределяющим, а не перегружающим, первым.

Python не поддерживает перегрузку методов, поэтому у вас есть как минимум два варианта.

  • Определить только конструктор varargs. Осмотрите длину списка аргументов и типы первых нескольких элементов, чтобы определить, какую логику нужно запустить. Фактически, вы объедините два конструктора в один.
  • Преобразовать один из конструкторов в статический метод factory. Например, вы можете удалить первый конструктор, сохранить varargs один, а затем определить новый метод factory.

Я предпочитаю второй метод, который позволяет вам четко разделить вашу логику. Вы также можете выбрать более описательное имя для вашего метода factory; 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)].

EDIT: Вы можете эмулировать перегрузку метода (через динамическую отправку, а не статическую отправку, поскольку вы можете использовать to, но статические типы не существуют в Python) с декораторами функций. Возможно, вам придется разработать собственное решение для поддержки *args и **kwargs, а отдельные методы с более точными именами все же часто являются лучшим решением.

  • 0
    Все объекты Die в коде OP не имеют состояния, поэтому N копий одного и того же Die должны давать те же результаты, что и N различных объектов Die .
  • 0
    На данный момент, конечно. Это похоже на потенциальную ловушку, и я бы использовал API, похожий на синглтон, чтобы выразить свое намерение, если бы можно было ссылаться на тот же Die; например, [Die.with_sides(sides)] * num_dice .
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)
  • 0
    Текущий конструктор self.die_list иногда делает self.die_list кортежем, а список - другим. Я бы постарался сделать так же каждый раз.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню