Question

Hi I have the following code, which attempts to create an instance of a class and assign argument values to it. I am trying to use *args to do this as follows:

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]

I get a "tuple index out of range" error. As you can see from the commented line, I was previously using positional arguments to accomplish this (which worked), and used lines like the following to do it:

    self.president = president

What is the right way to use *args in this case? should I be using *kwargs?

Was it helpful?

Solution

You're passing only one argument to President(), which is the list

['FDR', False, 4, 1933]

If you want to pass the items in that list as separate arguments, you do it like this:

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

As Colonel Panic points out, in your example the use of argument-unpacking is a little pointless - presumably your actual use case is more complex, and justifies its use.

UPDATE:

Your comment is really a follow-up question which would be better as a separate question, but:

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)

This shows how you can define default values as well.

OTHER TIPS

You should only use *args if you do not know how many arguments will be passed to function.

In your case, it looks like you need all of president, alive, terms, firstelected. There is nothing wrong with having a constructor that takes all of those as parameters.

*kwargs is used for a few reasons. One is if you have default values that should be used, unless the user wants to specify them.

For more info, see this question, this question, and the official documentation.


In response to your comment

I would recommend you do have the datedied property for each president. If they haven't died yet, then the value should be None. When you extend only certain instances with functionality, it becomes harder (but not impossible) to reason about the code.

Having said that, if you wanted arbitrary properties for each president that clearly won't be applicable to each instance, then you could use keyword arguments. But adding properties isn't limited to the constructor. I would simply use setattr() and getattr().

How to set class properties with 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

You're calling President(testdata), when you should be doing President(*testdata) in order to unpack the list as you call the constructor.

Right now, you're essentially passing a single argument (the list), hence the IndexError: you're passing a single argument, so args is equal to [testdata] and not testdata.


As mentioned in the other answer though, it's not very Pythonic to use *args in your constructor here. You know which arguments you expect, so just use these.

It's OK-ish to use it when you call the function, though.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top