python call a function with kwargs - python

I have a function:
def myfunc():
kwargs = {}
a = 1
b = 2
kwargs.update(a=a, b=b)
newfunc(**kwargs)
and my newfunc
def newfunc(**kwargs):
print a
Its not giving the value of a which is 1
whats wrong ?

It's because you didn't put any key, value in your dictionary, you should have written that :
def newfunc(**kwargs):
print kwargs["a"]
def myfunc():
kwargs = {"a" :1, "b": 2}
newfunc(**kwargs)
You can refer to this thread to understand kwargs better : Understanding kwargs in Python

You should add yor variables to your dictionary and print the item at position of the variable. To me it looks like your code should be written as:
def myfunc():
kwargs = {'a': 1,
'b': 2}
newfunc(**kwargs)
def newfunc(**kwargs):
print(kwargs['a'])
if(__name__ == '__main__'):
myfunc()
or your newfunc should have the arguments you want to fill with your kwargs dictionary like:
def myfunc():
kwargs = {'a': 1,
'b': 2}
newfunc(**kwargs)
def newfunc(a, b):
print(a)
if(__name__ == '__main__'):
myfunc()
Hope that helps.

You forgot to include the error. The Error would have been a NameError, a being undefined.
There's multiple things wrong with your code:
def myfunc():
kwargs = {}
a = 1
b = 2
this doesn't change the dictionary kwargs. This just created two new local names a and b. I guess what you wanted to have is:
kwargs = {}
kwargs["a"] = 1
kwargs["b"] = 2
EDIT: Your update does solve the issue above this line
Then:
def newfunc(**kwargs):
print a
Will give you an Error, because where should a come from?
Using **kwargs here just tells python to store all (not previously absorbed) arguments get stored in a new dictionary. What you hence want is either something like:
def newfunc(a,b):
print a
or
def newfunc(**kwargs):
print kwargs["a"]
Taking a look at your code, you seem to struggle with the concepts of how to deal with dictionaries. Maybe the question you're asking would be easier for you to answer yourself if your sat back and read a tutorial on python's dict

Two things.
First, your kwargs argument in myfunc is an empty dict, so you won't pass any parameters. If you want to pass the value of a and b to newfunc, you can either use
kwargs = {'a':1, 'b':2}
newfunc(**kwargs)
or
newfunc(a=1, b=2)
In both cases, you will get 'a' and 'b' in the kwargs dict of the newfunc function.
Second, you should extract your argument from the kwargs dict in newfunc. Something like print kwargs.get('a') should suffice.

Related

Default value of python get function [duplicate]

This question already has answers here:
dict.get() - default arg evaluated even upon success
(6 answers)
Closed 2 years ago.
If the default value passed to python get function (for dictionaries) is an expression, is it evaluated only when the key is not found? Let's consider the following example.
myDiction = {"a":1,"b":2}
val = myDiction.get("c",pow(2,3)-5)
Is that math expression pow(2,3)-5 evaluated or not?
It's not hard to test by passing your own function.
def someF():
print("called")
return 3
myDiction = {"a":1,"b":2}
myDiction.get("a",someF())
Prints: "called". So yes it is evaluated, like you would expect from any argument passed to to a function.
Why don't we find out?
>>> d = {1:2}
>>> d.get(1, print('default') or 3)
default
2
>>> d.get(2, print('default') or 3)
default
3
As you can see, function arguments are evaluated before being passed to the function.
It will be evaluated anyway.
Try this:
def test_func():
print("run")
return 3
myDiction = {"a": 1, "b": 2}
val = myDiction.get("a", test_func())
print(val)
You can see that even though a exists in the myDiction, the "run" message is printed.
I've been wishing for a "lazy" version of dict.get that would allow passing a callable which will be called only if the key is missing a very long time now.
We don't have it yet, but one can implement that quite easily.
from collections import UserDict
def lazy_default_func():
print('in lazy_default_func')
return 7
def lazy_default_func_with_args(*args, **kwargs):
print('in lazy_default_func_with_args', 'args', args, 'kwargs', kwargs)
return 7
class MyDict(UserDict):
def get(self, item, default=None, *args, **kwargs):
print('in get')
if item in self:
return self[item]
if not callable(default):
return default
return default(*args, **kwargs)
d = MyDict({'a': 1})
print(d.get('a', lazy_default_func))
print(d.get('b', lazy_default_func))
print(d.get('b', lazy_default_func_with_args, 'arg1'))
outputs
in get
1
in get
in lazy_default_func
7
in get
in lazy_default_func_with_args args ('arg1',) kwargs {}
7

Python 3 - **kwargs values can't be modified?

Practicing **kwargs notion in Python ,I wrote the following function :
def my_dictionary(**kwargs):
for i in kwargs.values():
i += 2
return kwargs
print(my_dictionary(a=1,b=2))
I get the following output :
{'b': 2, 'a': 1}
Why didn't the values in kwargs changed, as instructed (i += 2) ?
You are only making an amendment to the variable i in your example. You need to affect kwargs as so:
def my_dictionary(**kwargs):
for k in kwargs.keys():
kwargs[k] += 2
return kwargs

Make kwargs directly accessible

I am refactoring a piece of code, and I have run into the following problem. I have a huge parameter list, which now I want to pass as kwargs. The code is like this:
def f(a, b, c, ...):
print a
...
f(a, b, c, ...)
I am refactoring it to:
data = dict(a='aaa', b='bbb', c='ccc', ...)
f(**data)
Which means I have to do:
def f(**kwargs):
print kwargs['a']
...
But this is a pita. I would like to keep:
def f(**kwargs):
# Do some magic here to make the kwargs directly accessible
print a
...
Is there any straightforward way of making the arguments in the kwargs dict directly accessible, maybe by using some helper class / library?
There are some ways - but you can also wrap your function like this:
def f(**kwargs):
arg_order = ['a', 'b', 'c', ...]
args = [kwargs.get(arg, None) for arg in arg_order]
def _f(a, b, c, ...):
# The main code of your function
return _f(*args)
Sample:
def f(**kwargs):
arg_order = ['a', 'b', 'c']
args = [kwargs.get(arg, None) for arg in arg_order]
def _f(a, b, c):
print a, b, c
return _f(*args)
data = dict(a='aaa', b='bbb', c='ccc')
f(**data)
Output:
>>>
aaa bbb ccc
Well, you can update locals manually, but the documentation specifically warns against it.
for key, value in kwargs.iteritems(): #Python 2.7 here
locals()[key] = value
The other option is using exec, which though usually frowned on, is at least guaranteed to work correctly.
for key, value in kwargs.iteritems(): #Python 2.7 here
exec('{} = value'.format(key)
Though I wouldn't ever admit to anyone that you actually did either of these.
Inside the function:
for k, v in kwargs.iteritems():
locals()[k] = v
Another possible method (Based on your comment here) is to use an Attribute Dictionary:
class AttributeDict(dict):
def __getattr__(self, attr):
return self[attr] if attr in self.keys() else None
def __setattr__(self, attr, value):
self[attr] = value
To be used like this:
def f(**kwargs):
kwargs = AttributeDict(kwargs)
Sample:
def f(**kwargs):
kwargs = AttributeDict(kwargs)
print kwargs.a, kwargs.b, kwargs.c
data = dict(a='aaa', b='bbb', c='ccc')
f(**data)
Output:
>>>
aaa bbb ccc
Note: You can always name it x if you want a shorter variable name, then access would just be x.a
I have had to refactor my code in similar cases and I share your dislike of using kwargs['a'], which just feels a bit awkward. Sometimes I use a bunch object instead of a dictionary, which allows you to access fields by attribute access (params.a) instead of a dictionary lookup. It saves you only 3 characters of typing for every time you use a parameter, but I find it much better looking since you do not need quotes around the parameter name. There are various recipes around to implement one, see e.g. these ones.
So instead of using a dict like in your case, you would use it like
In [1]: class Bunch:
...: def __init__(self, **kwds):
...: self.__dict__.update(kwds)
In [2]: params = Bunch(a = 'aaa', b = 'bbb')
In [3]: def f(p):
...: print p.a
...:
In [4]: f(params)
aaa
I know this is not a direct answer to your question, but it is just an alternative for using kwargs.
It seems to me that you're doing redundand job, and easiest solution wouldb leave as it is. Those a, b, c are keyword arguments as well as positional, so you can call your function the way you like:
>>> def f(a, b, c=3):
... print a, b, c
With all keyword args
>>> f(**{'a':1, 'b':2})
1 2 3
With mix of positional and keyword args
>>> f(5, **{'b':4})
5 4 3
And get proper error in case of wrong keyword args
>>> f(**{'d':4, 'a':1, 'b':2})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() got an unexpected keyword argument 'd'

Alias for dictionary operation in Python

I want to do something like this:
f[frozenset((1,3,4))] = 5
f[frozenset((1,))] = 3
but it's just painful to type these all the time, is there anyway to have alias for this? I know in C++ it's possible to have a helper function which return a reference so you can just type:
F(1,3,4) = 5
F(1) = 3
with F as a helper function. Thanks very much!
I think that this can really only be achieved via a subclass:
class FrozenSetDict(dict):
def __setitem__(self,idx,value):
try:
dict.__setitem__(self,frozenset(idx),value)
except TypeError:
dict.__setitem__(self,frozenset((idx,)),value)
d = FrozenSetDict()
d[1,2,3] = 4
d[1] = 5
print d
yields:
{frozenset([1, 2, 3]): 4, frozenset([1]): 5}
This introduces an asymmetry between __getitem__ and __setitem__ which could easily be fixed by re-defining __getitem__ in the same way.
This might seem a little messy -- Indeed it is. Why require a subclass? That just makes it harder to put non-frozenset objects into your dictionary as keys. You could easily use this recipe though to create a proxy object which will do this with your dict:
#I don't like the name of this class -- I'm open to suggestions :)
class FrozenSetProxy(object):
def __init__(self,obj):
self.obj = obj
def __setitem__(self,idx,value):
try:
self.obj[frozenset(idx)] = value
except TypeError:
self.obj[frozenset((idx,))] = value
def __getitem__(self,idx):
try:
return self.obj[frozenset(idx)]
except TypeError:
return self.obj[frozenset((idx,))]
d = dict()
F = FrozenSetProxy(d)
F[1,2,3] = 4
F[1] = 5
print d
print F[1]
There's nothing like a C++ reference in Python, and the syntax you use is illegal to boot (in the words of the parser: can't assign to function call). You could emulate it with an object or subclass dict to customize its __getitem__. But there's a simpler and less intrusive way: Pass the value to the helper too, and let it handle the assignment:
def blah(f):
def F(*args, value):
f[frozenset(args)] = value
F(1, 3, 4, value=5)
F(1, value=3)
Note that this uses a Python 3 feature, keyword-only parameters. If you need it to work with Python 2, you can emulate the call syntax by accepting **kwdargs:
def F(*args, **kwds):
# optional: check that no other keyword arguments were passed
f[frozenset(args)] = kwds['value']

Conditional statements based on function parameter name [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to get the original variable name of variable passed to a function
Is there something like below in python where based on the name of the parameter passed to a function we can execute conditional statements like below.
def func(a,b,c,d)
if fourth paramter = d:
#code
elif fourth parameter = e:
#code
else:
#code
def main():
func(a,b,c,d)
func(a,b,c,e)
You should use keyword arguments:
def func(a, b, c, **kwargs):
if 'd' in kwargs:
....
elif 'c' in kwargs:
....
This is not exactly equivalent to what you want, though. If you want to ensure that d or e is the fourth parameter, that would be more troublesome.
Then you could try something like:
def func(a, b, c, d=None, e=None):
if d is not None and e is None:
....
elif e is not None and d is None:
....
Note that this is not exactly what you're asking. It will not access the "original" name of the variable passed to the function as a positional argument, as in the question linked by Stuart. You shouldn't even try to do that, it's pointless.
kwargs will not give you exactly what you want, but it can be close:
def func(a, b, c, **kwargs):
if 'd' in kwargs:
print "do something"
elif 'e' in kwargs:
print "do something else"
def main():
func(a = 1 ,b = 2 ,c = 3, d = 'some_value')
func(a = 1 ,b = 2 ,c = 3, e = 'some_value')

Categories

Resources