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
Related
I have a part of code in python, which calls dynamically different functions, where I always want to pass 3 different arguments. However, these functions, might not always need to use those 3 different arguments.
Here is a very simple code that shows the issue:
def test_keyword_args():
def fn1(a, b, c):
return a + b
def fn2(a, b, c):
return a + c
def fn3(a, b, c):
return b + c
obj = {
'a': fn1,
'b': fn2,
'c': fn3,
}
for key in obj:
value = obj[key](a=1, b=2, c=3)
if key == 'a':
assert value == 3
if key == 'b':
assert value == 4
if key == 'c':
assert value == 5
How can I always call same function obj[key](a=1,b=2,c=3) passing this keyword arguments, and avoid complains about unused parameters? (c not used in fn1, b not used in fn2, a not used in fn3)
I can imagine suppressing warnings would do the trick, but I do not think it is the appropriate solution
I am using Python 3.7.3
You can define arguments as keyword only by prefixing the argument list with *, you can then avoid the unused parameter warnings by naming a parameter _. Using **_ allows us to ignore any keyword arguments not in our named parameters
def fn1(*, a, b, **_):
return a + b
def fn2(*, a, c, **_):
return a + c
def fn3(*, b, c, **_):
return b + c
You may use kwargs to pass key word arguments to a function. In such case kwargs is the dictionary with named arguments passed to function.
def fn1(a, b, **kwargs):
return a + b
You can pass anything to this function in format fn1(a_value, b_value, any_parameter_1=100, any_parameter_2=100) etc. In function you receive your variables a and b and also kwargs dictionary with following content
{
"any_parameter_1": 100,
"any_parameter_2": 1000,
}
Also you may pass all variables as kwargs
def fn1(**kwargs):
return kwargs["a"] + kwargs["b"]
But you need to assign names to your parameters like this fn1(a=a_value, b=b_value, any_parameter_1=100, any_parameter_2=100) and kwargs dictionary will looks like
{
"a": a_value,
"b": b_value,
"any_parameter_1": 100,
"any_parameter_2": 1000,
}
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
So I have this function
def f(a=1, b=1):
print("a:", a, "| b:", b)
and this dictionary args = {"a": 4, "b": 5}. What I want is to make a loop to call f(a=4) in the first iteration and f(b=5) in the second. So basically something like this
for key,value in args.items():
f(key=value)
but somehow making key an argument instead of a string. The output should be:
a: 4 | b: 1
a: 1 | b: 5
but of course I'm getting TypeError: f() got an unexpected keyword argument 'key'.
I'm not entirely sure I understand your question, but I think you are looking for both of the uses of ** for keyword arguments.
def f(**kwargs):
for k, v in kwargs.items():
f(**{k: v})
Is there an equivalent to R's do.call in python?
do.call(what = 'sum', args = list(1:10)) #[1] 55
do.call(what = 'mean', args = list(1:10)) #[1] 5.5
?do.call
# Description
# do.call constructs and executes a function call from a name or a function and a list of arguments to be passed to it.
There is no built-in for this, but it is easy enough to construct an equivalent.
You can look up any object from the built-ins namespace using the __builtin__ (Python 2) or builtins (Python 3) modules then apply arbitrary arguments to that with *args and **kwargs syntax:
try:
# Python 2
import __builtin__ as builtins
except ImportError:
# Python 3
import builtins
def do_call(what, *args, **kwargs):
return getattr(builtins, what)(*args, **kwargs)
do_call('sum', range(1, 11))
Generally speaking, we don't do this in Python. If you must translate strings into function objects, it is generally preferred to build a custom dictionary:
functions = {
'sum': sum,
'mean': lambda v: sum(v) / len(v),
}
then look up functions from that dictionary instead:
functions['sum'](range(1, 11))
This lets you strictly control what names are available to dynamic code, preventing a user from making a nuisance of themselves by calling built-ins for their destructive or disruptive effects.
do.call is pretty much the equivalent of the splat operator in Python:
def mysum(a, b, c):
return sum([a, b, c])
# normal call:
mysum(1, 2, 3)
# with a list of arguments:
mysum(*[1, 2, 3])
Note that I’ve had to define my own sum function since Python’s sum already expects a list as an argument, so your original code would just be
sum(range(1, 11))
R has another peculiarity: do.call internally performs a function lookup of its first argument. This means that it finds the function even if it’s a character string rather than an actual function. The Python equivalent above doesn’t do this — see Martijn’s answer for a solution to this.
Goes similar to previous answer, but why so complicated?
def do_call(what, args=[], kwargs = {}):
return what(*args, **kwargs)
(Which is more elegant than my previously posted definition:)
def do_call(which, args=None, kwargs = None):
if args is None and kwargs is not None:
return which(**kwargs)
elif args is not None and kwargs is None:
return which(*args)
else:
return which(*args, **kwargs)
Python's sum is different than R's sum (1 argument a list expected vs.
arbitraily many arguments expected in R). So we define our own sum (mysum)
which behaves similarly to R's sum. In a similar way we define mymean.
def mysum(*args):
return sum(args)
def mymean(*args):
return sum(args)/len(args)
Now we can recreate your example in Python - as a reasonable 1:1 translation of the R function call.
do_call(what = mymean, args=[1, 2, 3])
## 2.0
do_call(what = mysum, args=[1, 2, 3])
## 6
For functions with argument names, we use a dict for kwargs, where the parameter
names are keys of the dictionary (as strings) and their values the values.
def myfunc(a, b, c):
return a + b + c
do_call(what = myfunc, kwargs={"a": 1, "b": 2, "c": 3})
## 6
# we can even mix named and unnamed parts
do_call(what = myfunc, args = [1, 2], kwargs={"c": 3})
## 6
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.