kargs and args behaving strange - python

I was just testing a piece of code with kwargs and args. But why args is picking up all the passed parameters in the following code ?
def myfunc(color='red',*args,**kwargs):
print (args)
print (color)
print (kwargs)
names=["Ramesh","Krishna"]
ages={'Ramesh_age':20,'Krishna_age':10,'Kanchi_age':5}
myfunc('blue',names,ages)
Result:
(['Ramesh', 'Krishna'], {'Krishna_age': 10, 'Ramesh_age': 20, 'Kanchi_age': 5})
blue
{}
why it all including ages has been assigned to ages and kwargs is empty ?

You need to use *names to unwrap names and **ages to unwrap ages.
Like this myfunc('blue', *names, **ages)

To be precise, kwargs is not picking up any values because kwargs looks for key - value pairs.
The way you passed your arguements, there is no key value pairs. You passed along
ages={'Ramesh_age':20,'Krishna_age':10,'Kanchi_age':5}
as a whole object, instead of destructuring the object and passing it as individual key value pairs.
As others have pointed out, the correct way is to destructure the object as seperate key value pairs as:
**ages

args looks for any values and kwargs looks for any key-value pairs. When you call your function, you have to specify * before args and ** before kwargs. It is the way Python works.
Here's your code:
def myfunc(color='red',*args,**kwargs):
print (args)
print (color)
print (kwargs)
names=["Ramesh","Krishna"]
ages={'Ramesh_age':20,'Krishna_age':10,'Kanchi_age':5}
# to call the function
myfunc('blue', *names, **ages)
Here's the results:
('Ramesh', 'Krishna')
blue
{'Ramesh_age': 20, 'Krishna_age': 10, 'Kanchi_age': 5}
Explanations can be found here and here

Related

keyword error generated when Passing a dictionary to a function with tuples as the keys

I am new to Python and I am struggling with the task of passing a dictionary, whose keys are tuples, as an argument to a function.
mydict = {('hostabc', 'pola'): 333444567, ('hostdef', 'polb'): 111222333, ('hostghi', 'polc'): 222999888}
def tupletest(**kwargs):
print(kwargs)
tupletest(**mydict)
The following keyword error is generated:
TypeError Traceback (most recent call last)
<ipython-input-29-fec409a1eb53> in <module>
2 def tupletest(**kwargs):
3 print(kwargs)
----> 4 tupletest(**mydict)
TypeError: tupletest() keywords must be strings
I am unsure if this is even possible given the error msg. I am testing this in 3.7.4
All help appreciated.
I performed a little example. It ist possible:
mydict = {('hostabc', 'pola'): 333444567, ('hostdef', 'polb'): 111222333, ('hostghi', 'polc'): 222999888}
def tupletest(kwargs):
for key in kwargs:
#print("key: %s , value: %s" % (key, kwargs[key]))
print(key[0])
print(key[1])
tupletest(mydict)
I hope this helps you. I also implemented a little example for entering the key of the dictionary.
Output
Short answer is, no it's not possible.
complex(real=3, imag=5)
complex(**{'real': 3, 'imag': 5})
**kwargs represents keyword arguments. These arguments are unpacked (so to say) and passed to the function. That way you can use them in the function without having to explicitly pass them to the function as the positional-or-keyword arguments.
def func(*args, **kwargs): ...
var-keyword: specifies that arbitrarily many keyword arguments can be
provided (in addition to any keyword arguments already accepted by
other parameters). Such a parameter can be defined by prepending the
parameter name with **, for example kwargs in the example above.
https://docs.python.org/3/glossary.html#term-argument
#BoarGules has nicely pointed you to the path. I have nothing new to add and I am saying below the same thing but in a little verbose manner.
See this nice discussion here. So dictionary keys become the named parameters to the function. In this particular case however the keys are tuples. The keyword must have an associated string property and that is what the error is saying above. Notice the "strings" in the error message below.
TypeError: tupletest() keywords must be strings
Had your dictionary been simpler like below, it would have worked.
mydict = {"a": 333444567, "b": 111222333, "c": 222999888}
def tupletest(**kwargs):
for k in kwargs:
print(k)
tupletest(**mydict)
The above gives this.
a
b
c
If you would rather want to have the tuples, I will take the dangerous route of eval after quoting the tuples.
mydict = {"('hostabc', 'pola')": 333444567, "('hostdef', 'polb')": 111222333, "('hostghi', 'polc')": 222999888}
def tupletest(**kwargs):
for k in kwargs:
print(eval(k))
tupletest(**mydict)
This gives the following output.
('hostabc', 'pola')
('hostdef', 'polb')
('hostghi', 'polc')

Calling Python function with set of parameters from variable

In my Python script I call this function a few times
write2db(int(pocnr), larm1, tid, label1, q1, x, y, lat, long, radio)
I want to be able to have the set of variables in one variable.
Should look like this
write2db(myargs)
So when I make a change to list of args I don't have to do it in more than one place. I have tried a few things but so far no luck.
Any suggestions?
You can use *args or **kwargs. The names args and kwargs don't actually matter, but its the * and ** that does the trick. Basically, * will unpack a list, and similarly, ** will unpack a dict.
*args is just a list of values in the same order as where you defined your function.
eg.
args = [23, 6, 2, "label", 5, 25, 21, 343.22, 111.34, 2]
write2db(*args)
**kwargs is a key-value mapping (python dict) of argument names to argument values
eg.
kwargs = {
'pocnr': 23,
'larm1': 21,
# ... etc.
}
write2db(**kwargs)
You could create a namedtuple with each of those arguments, then get the arguments out by name inside of the function.
Or you could just use variable length argument *args, and then inside your function:
for arg in args:
# do something with arg

Getting variable name in Python?

I have this method that takes in an unknown number of args and I have some variables from another class that I want to match the arg variable name to. The variable names are the same across this current class and the OtherClass. I was thinking something like:
def my_method(self, *args):
for arg in args:
OtherClass.arg_variable_name = arg # where "arg" on the right is the value
How could I do this effectively in python?
I think you should use **kwds instead of args. *args are values, so the caller can send it not with the variable, but just value itself.
my_object.my_method(1, 2, 3, "string")
If you use **kwds, you'll know the key, value pairs.
You could use something like
dict( (name,eval(name)) for name in['self','args']
To get a dict with the values for each argument, and then from there you could match the dict values with your respective arg variable name.

Python notation or operation "**"

I saw the following code:
def __init__(self, fn, **kw):
[setattr(self,k,v) for (k,v) in kw.items()]
......
What does the input argument **kw mean?
kw is bound to a dict mapping keyword argument names to their values.
Try calling
def return_kwargs(**kw):
return kw
as return_kwargs(foo=1, bar="2", baz="hamspamspam").
Suppose you have a dict kw = {'a':1,'b':2}, then calling myfunction(**kw) is equivalent to calling myfunction(a=1,b=2).
This particular construction means what all keyword arguments for the constructor will end up as object attributes.
foo = Foo( func, bar = 1, baz = "aa" )
this would create an object with attribute "bar" set to 1 and "baz" to "aa"
Inside the function, kw is a dictionary that contains all the keyword=value arguments that you gave to your function:
def demo(**kw):
print kw
demo(a=1, b="hello")
Run the above and it will display a dictionary with two keys, a and b. So it works as a way to accept any keyword argument you decide to use when you call the function.
That's what it does. Why would anyone want to do that? Perhaps in a function that calls another function (given as a separate argument), and **kw is to hold options for the second function.

In Python, what does dict.pop(a,b) mean?

class a(object):
data={'a':'aaa','b':'bbb','c':'ccc'}
def pop(self, key, *args):
return self.data.pop(key, *args)#what is this mean.
b=a()
print b.pop('a',{'b':'bbb'})
print b.data
self.data.pop(key, *args) ←------ why is there a second argument?
The pop method of dicts (like self.data, i.e. {'a':'aaa','b':'bbb','c':'ccc'}, here) takes two arguments -- see the docs
The second argument, default, is what pop returns if the first argument, key, is absent.
(If you call pop with just one argument, key, it raises an exception if that key's absent).
In your example, print b.pop('a',{'b':'bbb'}), this is irrelevant because 'a' is a key in b.data. But if you repeat that line...:
b=a()
print b.pop('a',{'b':'bbb'})
print b.pop('a',{'b':'bbb'})
print b.data
you'll see it makes a difference: the first pop removes the 'a' key, so in the second pop the default argument is actually returned (since 'a' is now absent from b.data).
So many questions here. I see at least two, maybe three:
What does pop(a,b) do?/Why are there a second argument?
What is *args being used for?
The first question is trivially answered in the Python Standard Library reference:
pop(key[, default])
If key is in the dictionary, remove it and return its value, else return default.
If default is not given and key is not in the dictionary, a KeyError is raised.
The second question is covered in the Python Language Reference:
If the form “*identifier” is present,
it is initialized to a tuple receiving
any excess positional parameters,
defaulting to the empty tuple. If the
form “**identifier” is present, it is
initialized to a new dictionary
receiving any excess keyword
arguments, defaulting to a new empty
dictionary.
In other words, the pop function takes at least two arguments. The first two get assigned the names self and key; and the rest are stuffed into a tuple called args.
What's happening on the next line when *args is passed along in the call to self.data.pop is the inverse of this - the tuple *args is expanded to of positional parameters which get passed along. This is explained in the Python Language Reference:
If the syntax *expression appears in
the function call, expression must
evaluate to a sequence. Elements from
this sequence are treated as if they
were additional positional arguments
In short, a.pop() wants to be flexible and accept any number of positional parameters, so that it can pass this unknown number of positional parameters on to self.data.pop().
This gives you flexibility; data happens to be a dict right now, and so self.data.pop() takes either one or two parameters; but if you changed data to be a type which took 19 parameters for a call to self.data.pop() you wouldn't have to change class a at all. You'd still have to change any code that called a.pop() to pass the required 19 parameters though.
def func(*args):
pass
When you define a function this way, *args will be array of arguments passed to the function. This allows your function to work without knowing ahead of time how many arguments are going to be passed to it.
You do this with keyword arguments too, using **kwargs:
def func2(**kwargs):
pass
See: Arbitrary argument lists
In your case, you've defined a class which is acting like a dictionary. The dict.pop method is defined as pop(key[, default]).
Your method doesn't use the default parameter. But, by defining your method with *args and passing *args to dict.pop(), you are allowing the caller to use the default parameter.
In other words, you should be able to use your class's pop method like dict.pop:
my_a = a()
value1 = my_a.pop('key1') # throw an exception if key1 isn't in the dict
value2 = my_a.pop('key2', None) # return None if key2 isn't in the dict
>>> def func(a, *args, **kwargs):
... print 'a %s, args %s, kwargs %s' % (a, args, kwargs)
...
>>> func('one', 'two', 'three', four='four', five='five')
a one, args ('two', 'three'), kwargs {'four': 'four', 'five': 'five'}
>>> def anotherfunct(beta, *args):
... print 'beta %s, args %s' % (beta, args)
...
>>> def func(a, *args, **kwargs):
... anotherfunct(a, *args)
...
>>> func('one', 'two', 'three', four='four', five='five')
beta one, args ('two', 'three')
>>>

Categories