6

I'd like to get idea why should I use kwargs or args over passing in a simple dict (or tuple in case of args)?

I wrote a very simple code snippet to check what exactly happens and I can't find any pros to use kwargs over a dict. If anyone could tell me why should I use those I'd be happy. Now as I can see it just more pythonic but don't makes any difference. Also if you use a simple dict then it's more readable because all the languages can do that but not the kwargs way.

def test_normal(input: dict):
    for element in input.items():
        print('Type: {}, raw: {}'.format(type(input), input))
        print('key: {}, value: {}'.format(element[0], element[1]))

def test_kwargs(**kwargs):
    for element in kwargs.items():
        print('Type: {}, raw: {}'.format(type(kwargs), kwargs))
        print('key: {}, value: {}'.format(element[0], element[1]))

test_normal(dict(name='Joseph'))
test_kwargs(name='Joseph')
Type: <class 'dict'>, raw: {'name': 'Joseph'}
key: name, value: Joseph
Type: <class 'dict'>, raw: {'name': 'Joseph'}
key: name, value: Joseph
Guigreg
  • 503
  • 6
  • 15
  • 3
    Because `test_kwargs(name='Joseph')` looks much better than `test_normal(dict(name='Joseph'))` – Yevhen Kuzmovych Sep 02 '19 at 12:21
  • Possible duplicate of [What does \*\* (double star/asterisk) and \* (star/asterisk) do for parameters?](https://stackoverflow.com/questions/36901/what-does-double-star-asterisk-and-star-asterisk-do-for-parameters) – buran Sep 02 '19 at 12:22
  • 1
    Your question is too broad, try expanding out your example to a function that requires more than 1 parameter for a start. – Sayse Sep 02 '19 at 12:31
  • The idea for `kwargs` is a clean interface to allow input parameters that aren't necessarily predetermined. This way you don't have to throw it in a dictionary. See [this post](https://stackoverflow.com/questions/1769403/what-is-the-purpose-and-use-of-kwargs) as well. – busybear Sep 02 '19 at 12:32
  • @busybear Yes, you don't have to throw it into a dictionary but when you try to use it in the method it will be a dictionary anyway. – Guigreg Sep 02 '19 at 14:02
  • @buran Not duplicate I guess because I'm not asking how it works but why or when to use it – Guigreg Sep 02 '19 at 14:05
  • @Guireg, check this question - same as yours https://stackoverflow.com/questions/37112721/what-is-the-difference-in-args-kwargs-vs-calling-with-tuple-and-dict which was declared duplicate and redirected to the one I refer – buran Sep 02 '19 at 14:08
  • Possible duplicate of [What is the difference in \*args, \*\*kwargs vs calling with tuple and dict?](https://stackoverflow.com/questions/37112721/what-is-the-difference-in-args-kwargs-vs-calling-with-tuple-and-dict) – Brian Tompsett - 汤莱恩 Sep 02 '19 at 20:54

1 Answers1

7

These are different things and both have their use-cases. Just a rule of thumb: If it looks like a function parameter, it should be a function parameter.

There are several neat use-cases for *args and **kwargs. One of which is passing through parameters that you don't care about at this point:

Say you have class Base and class A inherited from Base:

class Base:
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

class A(Base):
    def __init__(self, n, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.n = n

As you see class A does not care about Base's __init__ parameters, so it just passes everything (except n that it needs) forward. So if you were to change Base's __init__ you would not need to change A.

But when you create A object you would pass parameters normally:

a = A(5, 3, y=6, z=42)

A similar idea is when you implement a decorator which you'd like to use on a function with any kind and number of arguments:

def say_hello_first(fn):
    def wrapper(*args, *kwargs):
        print('Hello')
        return fn(*args, **kwargs)
    return wrapper

@say_hello_first
def foo(x):
    print(x)

@say_hello_first
def bar(a, b, c=3.14):
    print((a + b) * c)

then:

>>> foo(42)
Hello
42

>>> bar(1, 2, c=3)
Hello
9
Yevhen Kuzmovych
  • 10,940
  • 7
  • 28
  • 48