2011-01-29 2 views
1

Вот то, что я до сих пор:Почему у меня нет конструктора varargs и другого конструктора с фиксированными аргументами?

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 first. В определении класса все методы уникальны, там нет двух конструкторов (они на самом деле являются инициализаторами). Ваш 'die_list' является кортежем целых чисел, которые проходят. – SilentGhost

ответ

3

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

Python doesn't support method overloading, поэтому у вас есть как минимум два варианта.

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

Я предпочитаю второй метод, который позволяет вам чисто отделить вашу логику. Вы также можете выбрать более описательное имя для своего заводского метода; from_n_sided_dice является более информативным, чем просто Dice:

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

Side Примечание: Является ли это действительно то, что вы хотите? [Die(sides)] * num_dice возвращает список с несколькими ссылками на тот же объект Die. Скорее, вам может понадобиться [Die(sides) for _ in range(num_dice)].

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

+0

Все объекты 'Die' в коде OP не имеют аналогов, поэтому N копий одного и того же' Die' должны давать те же результаты, что и N различных объектов 'Die'. –

+0

На данный момент, конечно. Это похоже на потенциальную ловушку, и я бы использовал однопользовательский API, чтобы выразить свое намерение, если было бы хорошо ссылаться на тот же Die; например, '[Die.with_sides (стороны)] * num_dice'. – ide

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

Yorr current constructor иногда делает 'self.die_list' кортеж и список других времен. Я бы постарался сделать то же самое каждый раз. –

Смежные вопросы