If this is a duplicate question, my apologies in advance.. I would like to understand the **kwargs thing better in python and was actually hoping to be able to write a function that could handle unpacking and internally referencing the key:value
pairs from passing in **kwargs arguments.
My little example is as follows:
arguments = {'apple': 50, 'zoom': 'foo', 'bar': True}
I would like to be able to reference the keys and values of the dictionary passed into the function without explicitly referring to them from within the function itself.
def test(**kwargs):
if kwargs is not None:
print(f'apple: {apple}')
print(f'zoom: {zoom}')
print(f'bar: {bar}')
Calling the above via test(**arguments)
of course fails, but is there a way to "unpack" the **kwargs into the local namespace within the function to accomplish what I am thinking?
One thing I've considered is to create a dictionary within the function and then use exec()
to compile the key:value pairs into this namespace dictionary, but that seems a bit hairy.
Is there another way to assign local variables from **kwargs directly into a function?
Thanks in advance.
EDIT
I've also explored using vars()
and have written the following:
def test(**kwargs):
try:
print(**vars())
except Exception as e:
print(e, '\n\n')
print(vars())
if kwargs is not None:
try:
print(f'apple: {apple}')
print(f'zoom: {zoom}')
print(f'bar: {bar}')
except:
for k, v in kwargs.items():
print(f'k: {k} : v: {v}')
With the following output:
'kwargs' is an invalid keyword argument for this function
{'kwargs': {'apple': 50, 'zoom': 'foo', 'bar': True}, 'e':TypeError("'kwargs' is an invalid keyword argument for this function",)}
k: apple : v: 50
k: zoom : v: foo
k: bar : v: True
I notice that print(**kwargs)
is trying to access "kwargs" as a named variable. I guess the question I am trying to resolve is if defining the function with default arguments, then passing in **kwargs would solve the issue? I can try this and see if it works and update..
EDIT II
This works..It looks like defining the function to have named defaults allowed me to reference those variables directly from **kwargs while not doing so entailed referencing the **kwargs dictionary and knowing the keys anyway.
arguments = {'apple': 50, 'zoom': 'foo', 'bar': True, 'baz': 99}
def test(apple = None, zoom = 'fee', bar = False, **kwargs):
if kwargs is not None:
try:
print(f'apple: {apple}')
print(f'zoom: {zoom}')
print(f'bar: {bar}')
except Exception as e:
print(e, '\n\n\n')
for k, v in kwargs.items():
print(f'k: {k} : v: {v}')
test(**arguments)
EDIT III: update
The following is the closest to what I had initially tried to convey and implement. A function that handles arbitrary user keyworded arguments and binds them to variables in a local namespace...
def test(**kwargs):
ns = {}
args_string = ''
for k, v in kwargs.items():
print(f'k: {k}, v: {v}')
try:
args_string += str(k) + '=' + '{}'.format(int(v))+'\n'
except:
args_string += str(k) + '=' + '"{}"'.format(str(v))+'\n'
exec(args_string, ns)
return {k:v for k, v in ns.items() if k in kwargs.keys() }
Running the following allows me access the variables, but I suspect this is not safe or "good" code.
locals().update(test(**arguments))
kwargs
is a dict, so you can simply refer to the values within by their keys:
def test(**kwargs):
if kwargs:
print(f'apple: {kwargs.get("apple")}')
print(f'zoom: {kwargs.get("zoom")}')
print(f'bar: {kwargs.get("bar")}')