распаковка аргументов и присвоение переменных класса

Привет, у меня есть следующий код, который пытается создать экземпляр класса и присвоить ему значения аргументов. Я пытаюсь использовать *args для этого следующим образом:

def main():
    testdata = ['FDR', False, 4, 1933]
    apresident = President(testdata)
    print apresident
    print apresident.alive

class President:
    id_iter = itertools.count(1)
    #def __init__(self, president, alive, terms, firstelected):
    def __init__(self, *args):
        self.id = self.id_iter.next()
        self.president = args[0]
        self.alive = args[1]
        self.terms = args[2]
        self.firstelected = args[3]

Я получаю сообщение об ошибке "индекс кортежа вне допустимого диапазона". Как вы можете видеть из закомментированной строки, ранее я использовал позиционные аргументы для достижения этой цели (что сработало) и использовал для этого такие строки, как следующие:

    self.president = president

Как правильно использовать *args в этом случае? я должен использовать *kwargs?


person domoarigato    schedule 24.08.2013    source источник
comment
Итак, я вижу, что добавление * - это то, как вы заставляете его работать так, как я задумал. Я намерен расширить функциональность класса и хотел бы добавить к нему дополнительные свойства позже, все из которых могут в какой-то момент не понадобиться для каждого экземпляра президента, для этого игрушечного примера, датированного. Я начинаю думать, что мне следует использовать *kwargs для этой цели, но как мне тогда назначить их внутри класса?   -  person domoarigato    schedule 24.08.2013


Ответы (3)


Вы передаете только один аргумент President(), то есть список

['FDR', False, 4, 1933]

Если вы хотите передать элементы в этом списке как отдельные аргументы, вы делаете это следующим образом:

    apresident = President(*testdata)  # note the * character

Как указывает полковник Паник, в вашем примере использование распаковки аргументов немного бессмысленно - предположительно ваш фактический вариант использования более сложен и оправдывает его использование.

ОБНОВЛЕНИЕ:

Ваш комментарий на самом деле является дополнительным вопросом, который был бы лучше как отдельный вопрос, но:

def main():
    testdata = {
        "president": "FDR",
        "alive": False,
        "terms": 4,
        "firstelected": 1933,
    }
    apresident = President(**testdata)
    anotherpresident = President(president="BHO", terms=2, firstelected=2008)
    print apresident
    print apresident.alive
    print anotherpresident
    print anotherpresident.alive

class President:
    id_iter = itertools.count(1)
    #def __init__(self, president, alive, terms, firstelected):
    def __init__(self, **kwargs):
        self.id = self.id_iter.next()
        self.president = kwargs.get("president", None)
        self.alive = kwargs.get("alive", True)
        self.terms = kwargs.get("president", 1)
        self.firstelected = kwargs.get("president", None)

Это также показывает, как вы можете определить значения по умолчанию.

person Zero Piraeus    schedule 24.08.2013
comment
это именно то, что я пытаюсь сделать. Я выбрал этот ответ по этой причине, хотя я согласен с тем, что ответ полковника Паника также ответил на мой первоначальный вопрос в том виде, в каком он был изначально задан. У меня недостаточно репутации, чтобы проголосовать за него. - person domoarigato; 24.08.2013

Вы должны использовать *args только в том случае, если вы не знаете, сколько аргументов будет передано в функцию.

В вашем случае похоже, что вам нужны все president, alive, terms, firstelected. Нет ничего плохого в конструкторе, который принимает все это в качестве параметров.

*kwargs используется по нескольким причинам. Во-первых, если у вас есть значения по умолчанию, которые следует использовать, если только пользователь не захочет их указать.

Для получения дополнительной информации см. этот вопрос, этот вопрос и официальная документация.


В ответ на ваш комментарий

Я бы порекомендовал вам сделать свойство datedied для каждого президента. Если они еще не умерли, то значение должно быть None. Когда вы расширяете только определенные экземпляры с функциональностью, становится сложнее (но не невозможно) рассуждать о коде.

Сказав это, если вам нужны произвольные свойства для каждого президента, которые явно не будут применимы к каждому экземпляру, вы можете использовать аргументы ключевого слова. Но добавление свойств не ограничивается конструктором. Я бы просто использовал setattr() и getattr().

Как установить свойства класса с помощью kwargs

class President(object):
   def __init__(self, *args, **kwargs):
      for name, value in kwargs.items():
         # Make each keyword-argument a property of the class.
         setattr(self, name, value)

tVar = President(is_cool=True)
print tVar.is_cool # Returns True
person Colonel Panic    schedule 24.08.2013
comment
Справедливый, и, возможно, это плохой пример. В конечном счете, id хотел бы создать экземпляр класса, используя довольно произвольные свойства, независимо от порядка, в котором эти свойства могут появляться в списке или словаре, который им передается, чтобы не слишком возиться с базовым классом. Если неназначенные свойства окажутся равными None, меня это устроит. - person domoarigato; 24.08.2013
comment
Я отредактировал вопрос, чтобы показать, что вы хотите делать с kwargs. - person Colonel Panic; 24.08.2013
comment
спасибо, я тоже проголосовал за ваш ответ, это было очень полезно - person domoarigato; 24.08.2013

Вы вызываете President(testdata) вместо того, чтобы делать President(*testdata), чтобы распаковать список при вызове конструктора.

Прямо сейчас вы, по сути, передаете один аргумент (список), поэтому IndexError: вы передаете один аргумент, поэтому args равно [testdata], а не testdata.


Однако, как упоминалось в другом ответе, здесь не очень питонично использовать *args в вашем конструкторе. Вы знаете, какие аргументы вы ожидаете, поэтому просто используйте их.

Тем не менее, его можно использовать при вызове функции.

person Thomas Orozco    schedule 24.08.2013