0

For the ease of further scale, I define a class Book with args and 'kwargs'.

class Book:
    def __init__(self, *args, **kwargs):
        if args:
            self.name,\
            self.author,\
            = args
        elif kwargs:
            self.__dict__.update(kwargs)

It works well respectively with positional and keywords arguments

In [62]: book1 = Book('Python', 'Guido')
In [63]: book1.author
Out[63]: 'Guido'

In [65]: book2 = Book(name='Python', author='Guido')
In [66]: book2.name
Out[66]: 'Python'

When test with mixture of positional and keywords arguments,error reports.

In [67]: book3 = Book('Python', author='Guido')
ValueError: not enough values to unpack (expected 2, got 1)

The bug can be fixed with multiple conditions or a standard definition class without taking *args or 'kwargs'.

How to fix it in an elegant method?

AbstProcDo
  • 19,953
  • 19
  • 81
  • 138
  • 1
    Is it a hard requirement that you use `*args` and `**kwargs`? Because the way you are trying to use them does not match the way in which they were intended/designed. – SethMMorton Nov 09 '17 at 06:51
  • 1
    So what's the initial intent of them? any material introduced? – AbstProcDo Nov 09 '17 at 06:53
  • 2
    You're trying to unpack: `self.name, self.author = ('Python',)`. Make sure that the input is what you're expecting... check `len(args)` first. – CristiFati Nov 09 '17 at 06:54
  • If you cannot control the number of arguments, passing in a dict can be a solution. – Yuan Fu Nov 09 '17 at 06:57
  • 1
    @DFK One use for `*args` is for situations where you need to accept an arbitrary number of arguments that you would then process anonymously (possibly in a `for` loop or something like that). `**kwargs` could be for when you need to accept arbitrary named parameters, or if the parameter list is too long for a standard signature. Or you might use the combination of both if you wrote a function wrapper and just want to pass the arguments given to your function to the wrapped function without recreating the signature. – SethMMorton Nov 09 '17 at 07:01
  • 1
    Check out https://stackoverflow.com/questions/36901/what-does-double-star-asterisk-and-star-asterisk-do-for-parameters, https://stackoverflow.com/questions/3394835/args-and-kwargs, and https://stackoverflow.com/questions/287085/what-do-args-and-kwargs-mean. – SethMMorton Nov 09 '17 at 07:03

3 Answers3

3

If your goal is to accept optional name and author arguments, you should use default arguments:

class Book(object):

    def __init__(self, name=None, author=None):
        self.name = name
        self.author = author

Not only does this make the class definition simpler, it also conveys better intent in the __init__ signature:

>>> c = Book("best book", "me")
>>> c.name
'best book'
>>> c.author
'me'
>>> c = Book(author="me", name="best book")
>>> c.name
'best book'
>>> c.author
'me'
>>> c = Book("best_book", author="me")
>>> c.name
'best book'
>>> c.author
'me'
bow
  • 2,503
  • 4
  • 22
  • 26
2

I would advise against *args and **kwargs here, since the way you wish to use them is not they way they were intended to be used. Just use named arguments and you will be able to do all that you want.

class Book:
    def __init__(self, name, author):
        self.name = name
        self.author = author

Works as expected.

In [2]: book1 = Book('Python', 'Guido')

In [3]: book2 = Book(name='Python', author='Guido')

In [4]: book3 = Book('Python', author='Guido')

In [7]: book1.name == book2.name
Out[7]: True

In [8]: book1.name == book3.name
Out[8]: True

In [9]: book2.name == book3.name
Out[9]: True
SethMMorton
  • 45,752
  • 12
  • 65
  • 86
1

I'd advise against just args and kwargs. But just in case you absolutely need it, here's one way:

class Book:
    def __init__(self, *args, **kwargs):
       attributes = ['name', 'author']
       for item in zip(attributes, args):
           kwargs[item[0]] = item[1]
       self.name = kwargs.get('name')
       self.author = kwargs.get('author')

    def show(self):
        print('Name={name}, author={author}'.format(name=self.name, author=self.author))

book1 = Book('Python', 'Guido')
book1.show()
book2 = Book(name='Python', author='Guido')
book2.show()
book3 = Book('Python', author='Guido')

Execution output:

Name=Python, author=Guido
Name=Python, author=Guido
Name=Python, author=Guido
Sharad
  • 9,282
  • 3
  • 19
  • 36