When to use * and ** as a function argument in python function - python

When can I pass * and ** in the argument of a Python function? i.e.:
def fun_name(arg1, *arg2 , ** arg3):

As you've stated your question, you aren't using them in the arguments (which occur when you are calling a function), you are using them in the parameters which occur when you are creating a function. The * and ** operators serve different purposes in each of those situations.
When you are defining a function, they specify that positional arguments will be placed in a tuple and that keyword arguments will be placed in a dict. Yes I did just say arguments, but they are applied to paramaters in this case.
def example(*args, **kwargs):
print "args: {0}".format(args)
print "kwargs: {0}".format(kwargs)
example(1, 2, 'a', foo='bar', bar='foo')
when run, this outputs:
args: (1, 2, 'a')
kwargs: {'foo': 'bar', 'bar': 'foo'}
Do you see what I mean when I say that we applied it to the paramaters in the function definition? the arguments are 1, 2, 'a', foo='bar', bar='foo'. the paramaters are *args, **kwargs.
Now here's an example applying them to arguments.
def example2(a, b, foo=None, bar=None):
print "a: {0}, b:{1}, foo:{2}, bar: {3}".format(a, b, foo, bar)
args = (1, 2)
kwargs = {'foo': 'bar', 'bar': 'foo'}
example2(*args, **kwargs)
This outputs:
a: 1, b:2, foo:bar, bar: foo
You can see that when we apply them to arguments (that is when we are calling the function), * has the effect of expanding a list or tuple to fill the positional arguments of a function and ** has the effect of expanding a dictionary to fill the keyword arguments of a function. You just need to make sure that there are enough and not too much arguments in total after the expansions have taken place.
in the last example, the arguments are *args, **kwargs and the parameters are a, b, foo=None, bar=None

When can I pass * and ** in the argument of a Python function? i.e.:
Short answer: when you require variable number of argument to be passed to your function.
That said, I honestly think that this a very broad question. You will be much better off reading more about these concepts, trying them off and then asking an specific questions here.
#Wooble's answer will help you; but what will help you even more is to understand what *args and **kwargs do. Then you can use them as befits the situations you encounter.
You can learn more about the variable and keyword arguments concepts for example at:
http://www.network-theory.co.uk/docs/pytut/KeywordArguments.html
http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/

Any time you want your function to accept non-named positional arguments (*), or additional named arguments (**).
It's certainly possible to include *args, **kwargs in every function definition, although this might not be a great idea if passing more arguments won't actually have any effect.

Related

Can I pass a function and a varying number of arguments to another function?

can someone say what the best method to do this would be?
I want to have a function, overall_function that I pass other functions to. But the other functions don't always have the same or type of arguments.
So what would be the correct syntax to do:
def overall_function(function, arguments):
function(arguments)
do other stuff
I'd like arguments to be:
arg1 = 'foo', arg2 = 53, ...
Thank you!
Take variable number of arguments:
def overall_function(function, *positional, **keyword):
function(*positional, **keyword)
Now, you can pass the arguments like:
overall_function(function, 1, 2, foo='bar')
and this will execute the function as:
function(1, 2, foo='bar')
positional would be a tuple: (1, 2) (these two are positional arguments).
keyword would be a dict: {'foo': 'bar'} (this is a keyword argument).
Looking for *args and **kwargs
def overall_function(function, *args, **kwargs):
function(*args, **kwargs)
do other stuff
You might be better off passing in a lambda as opposed to two arguments so you can delay the execution of the function till you need it, and still keeping the parameters contained together
def overall_function(lambda_func):
lambda_func()
overall_function(lambda: function(used,as,normal))

Meaning of unused * as function parameter [duplicate]

What does a bare asterisk in the parameters of a function do?
When I looked at the pickle module, I see this:
pickle.dump(obj, file, protocol=None, *, fix_imports=True)
I know about a single and double asterisks preceding parameters (for variable number of parameters), but this precedes nothing. And I'm pretty sure this has nothing to do with pickle. That's probably just an example of this happening. I only learned its name when I sent this to the interpreter:
>>> def func(*):
... pass
...
File "<stdin>", line 1
SyntaxError: named arguments must follow bare *
If it matters, I'm on python 3.3.0.
Bare * is used to force the caller to use named arguments - so you cannot define a function with * as an argument when you have no following keyword arguments.
See this answer or Python 3 documentation for more details.
While the original answer answers the question completely, just adding a bit of related information. The behaviour for the single asterisk derives from PEP-3102. Quoting the related section:
The second syntactical change is to allow the argument name to
be omitted for a varargs argument. The meaning of this is to
allow for keyword-only arguments for functions that would not
otherwise take a varargs argument:
def compare(a, b, *, key=None):
...
In simple english, it means that to pass the value for key, you will need to explicitly pass it as key="value".
def func(*, a, b):
print(a)
print(b)
func("gg") # TypeError: func() takes 0 positional arguments but 1 was given
func(a="gg") # TypeError: func() missing 1 required keyword-only argument: 'b'
func(a="aa", b="bb", c="cc") # TypeError: func() got an unexpected keyword argument 'c'
func(a="aa", b="bb", "cc") # SyntaxError: positional argument follows keyword argument
func(a="aa", b="bb") # aa, bb
the above example with **kwargs
def func(*, a, b, **kwargs):
print(a)
print(b)
print(kwargs)
func(a="aa",b="bb", c="cc") # aa, bb, {'c': 'cc'}
Semantically, it means the arguments following it are keyword-only, so you will get an error if you try to provide an argument without specifying its name. For example:
>>> def f(a, *, b):
... return a + b
...
>>> f(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 2 were given
>>> f(1, b=2)
3
Pragmatically, it means you have to call the function with a keyword argument. It's usually done when it would be hard to understand the purpose of the argument without the hint given by the argument's name.
Compare e.g. sorted(nums, reverse=True) vs. if you wrote sorted(nums, True). The latter would be much less readable, so the Python developers chose to make you to write it the former way.
Suppose you have function:
def sum(a,key=5):
return a + key
You can call this function in 2 ways:
sum(1,2) or sum(1,key=2)
Suppose you want function sum to be called only using keyword arguments.
You add * to the function parameter list to mark the end of positional arguments.
So function defined as:
def sum(a,*,key=5):
return a + key
may be called only using sum(1,key=2)
I've found the following link to be very helpful explaining *, *args and **kwargs:
https://pythontips.com/2013/08/04/args-and-kwargs-in-python-explained/
Essentially, in addition to the answers above, I've learned from the site above (credit: https://pythontips.com/author/yasoob008/) the following:
With the demonstration function defined first below, there are two examples, one with *args and one with **kwargs
def test_args_kwargs(arg1, arg2, arg3):
print "arg1:", arg1
print "arg2:", arg2
print "arg3:", arg3
# first with *args
>>> args = ("two", 3,5)
>>> test_args_kwargs(*args)
arg1: two
arg2: 3
arg3: 5
# now with **kwargs:
>>> kwargs = {"arg3": 3, "arg2": "two","arg1":5}
>>> test_args_kwargs(**kwargs)
arg1: 5
arg2: two
arg3: 3
So *args allows you to dynamically build a list of arguments that will be taken in the order in which they are fed, whereas **kwargs can enable the passing of NAMED arguments, and can be processed by NAME accordingly (irrespective of the order in which they are fed).
The site continues, noting that the correct ordering of arguments should be:
some_func(fargs,*args,**kwargs)

In Python, how to add a keyword argument to a function with *args and **kwargs? [duplicate]

In python we can do this:
def myFun1(one = '1', two = '2'):
...
Then we can call the function and pass the arguments by their name:
myFun1(two = 'two', one = 'one')
Also, we can do this:
def myFun2(**kwargs):
print kwargs.get('one', 'nothing here')
myFun2(one='one')
So I was wondering if it is possible to combine both methods like:
def myFun3(name, lname, **other_info):
...
myFun3(lname='Someone', name='myName', city='cityName', otherInfo='blah')
In general what combinations can we do?
Thanks and sorry for my silly question.
The general idea is:
def func(arg1, arg2, ..., kwarg1=default, kwarg2=default, ..., *args, **kwargs):
...
You can use as many of those as you want. The * and ** will 'soak up' any remaining values not otherwise accounted for.
Positional arguments (provided without defaults) can't be given by keyword, and non-default arguments can't follow default arguments.
Note Python 3 also adds the ability to specify keyword-only arguments by having them after *:
def func(arg1, arg2, *args, kwonlyarg=default):
...
You can also use * alone (def func(a1, a2, *, kw=d):) which means that no arguments are captured, but anything after is keyword-only.
So, if you are in 3.x, you could produce the behaviour you want with:
def myFun3(*, name, lname, **other_info):
...
Which would allow calling with name and lname as keyword-only.
Note this is an unusual interface, which may be annoying to the user - I would only use it in very specific use cases.
In 2.x, you would need to manually make this by parsing **kwargs.
You can add your named arguments along with kwargs. If the keys are available in the calling function It will taken to your named argument otherwise it will be taken by the kwargs dictionary.
def add(a=1, b=2,**c):
res = a+b
for items in c:
res = res + c[items]
print(res)
add(2,3)
5
add(b=4, a =3)
7
add(a =1,b=2,c=3,d=4)
10
It's possible at least for Python 2.7. Keyword arguments get assigned to positional parameters by name, so you can do
In [34]: def func(name, lname, **kwargs):
print 'name='+name, 'lname='+lname
print kwargs
....:
In [35]: func(lname='lname_val', name='name_val', city='cityName', otherInfo='blah')
name=name_val lname=lname_val
{'city': 'cityName', 'otherInfo': 'blah'}
Official docs state it that way:
"If keyword arguments are present, they are first converted to positional arguments, as follows. First, a list of unfilled slots is created for the formal parameters. If there are N positional arguments, they are placed in the first N slots. Next, for each keyword argument, the identifier is used to determine the corresponding slot (if the identifier is the same as the first formal parameter name, the first slot is used, and so on). If the slot is already filled, a TypeError exception is raised. Otherwise, the value of the argument is placed in the slot, filling it (even if the expression is None, it fills the slot)."
https://docs.python.org/2/reference/expressions.html#calls

Python: Passing parameters by name along with kwargs

In python we can do this:
def myFun1(one = '1', two = '2'):
...
Then we can call the function and pass the arguments by their name:
myFun1(two = 'two', one = 'one')
Also, we can do this:
def myFun2(**kwargs):
print kwargs.get('one', 'nothing here')
myFun2(one='one')
So I was wondering if it is possible to combine both methods like:
def myFun3(name, lname, **other_info):
...
myFun3(lname='Someone', name='myName', city='cityName', otherInfo='blah')
In general what combinations can we do?
Thanks and sorry for my silly question.
The general idea is:
def func(arg1, arg2, ..., kwarg1=default, kwarg2=default, ..., *args, **kwargs):
...
You can use as many of those as you want. The * and ** will 'soak up' any remaining values not otherwise accounted for.
Positional arguments (provided without defaults) can't be given by keyword, and non-default arguments can't follow default arguments.
Note Python 3 also adds the ability to specify keyword-only arguments by having them after *:
def func(arg1, arg2, *args, kwonlyarg=default):
...
You can also use * alone (def func(a1, a2, *, kw=d):) which means that no arguments are captured, but anything after is keyword-only.
So, if you are in 3.x, you could produce the behaviour you want with:
def myFun3(*, name, lname, **other_info):
...
Which would allow calling with name and lname as keyword-only.
Note this is an unusual interface, which may be annoying to the user - I would only use it in very specific use cases.
In 2.x, you would need to manually make this by parsing **kwargs.
You can add your named arguments along with kwargs. If the keys are available in the calling function It will taken to your named argument otherwise it will be taken by the kwargs dictionary.
def add(a=1, b=2,**c):
res = a+b
for items in c:
res = res + c[items]
print(res)
add(2,3)
5
add(b=4, a =3)
7
add(a =1,b=2,c=3,d=4)
10
It's possible at least for Python 2.7. Keyword arguments get assigned to positional parameters by name, so you can do
In [34]: def func(name, lname, **kwargs):
print 'name='+name, 'lname='+lname
print kwargs
....:
In [35]: func(lname='lname_val', name='name_val', city='cityName', otherInfo='blah')
name=name_val lname=lname_val
{'city': 'cityName', 'otherInfo': 'blah'}
Official docs state it that way:
"If keyword arguments are present, they are first converted to positional arguments, as follows. First, a list of unfilled slots is created for the formal parameters. If there are N positional arguments, they are placed in the first N slots. Next, for each keyword argument, the identifier is used to determine the corresponding slot (if the identifier is the same as the first formal parameter name, the first slot is used, and so on). If the slot is already filled, a TypeError exception is raised. Otherwise, the value of the argument is placed in the slot, filling it (even if the expression is None, it fills the slot)."
https://docs.python.org/2/reference/expressions.html#calls

Dynamically define functions with varying signature

What I want to accomplish:
dct = {'foo':0, 'bar':1, 'baz':2}
def func(**dct):
pass
#function signature is now func(foo=0, bar=1, baz=2)
However, the ** syntax is obviously clashing here between expanding a dict (what I want to do) and declaring a parameter that holds the keyword arguments (what I don't want to do).
Is this possible?
Based on my interpretation of your requirements -- you want to dynamically define a function with a signature that matches the content of adict provided at runtime -- there are two issues here which makes it impractical.
If the arguments are defined at run-time, how can your function reference the variables? Are you planning to build the function body at run-time as well?
dicts are unordered, so you cannot reliably use them to define positional arguments
I suspect this is an XY problem. If you can explain what you're trying to achieve then perhaps we can be of better help.
However, assuming you're trying to assign default keyword arguments using a dict then one way to achieve this would be to use decorators. For example:
def defaultArgs(default_kw):
"decorator to assign default kwargs"
def wrap(f):
def wrapped_f(**kwargs):
kw = {}
kw.update(default_kw) # apply defaults
kw.update(kwargs) # apply from input args
f(**kw) # run actual function with updated kwargs
return wrapped_f
return wrap
You can then use this decorator to assign default keyword arguments to a function that expects only keyword arguments:
defaults = {'foo':0, 'bar':1, 'baz':2}
#defaultArgs(defaults)
def func(**kwargs):
print kwargs # args accessible via the kwargs dict
Results:
func() # prints {'baz': 2, 'foo': 0, 'bar': 1}
func(foo=2) # prints {'baz': 2, 'foo': 2, 'bar': 1}
params = {'bar':1000, 'hello':'world'}
func(**params) # prints {'baz': 2, 'foo': 0, 'bar': 1000, 'hello': 'world'}
Note that you will not be able to use positional arguments:
func(1, 2, 3) # raises TypeError
what you want i believe is eval() link
an answer i gave on a similar question: https://stackoverflow.com/a/11865190/1561176
I'm really not sure what you plan on accomplishing here. The following works (sort of):
def func(**dct):
pass
dct = {'foo':0, 'bar':1, 'baz':2}
func(**dct)
How do you plan on using foo, bar or baz in the function if they're created dynamically? If you give some more details on what you're actually trying to accomplish, we might be able to be a little more helpful.

Categories

Resources