Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the benefit of using kwargs (or args) over a simple dict?

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
like image 492
Guigreg Avatar asked Sep 02 '19 12:09

Guigreg


1 Answers

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
like image 93
Yevhen Kuzmovych Avatar answered Oct 04 '22 21:10

Yevhen Kuzmovych