difference bewteen *args and **args in python? [duplicate] - python

This question already has answers here:
Use of *args and **kwargs [duplicate]
(11 answers)
Closed 9 years ago.
I am trying to understand the difference between *args and **args in function definition in python. In the below example, *args works to pack into a tuple and calculate the sum.
>>> def add(*l):
... sum = 0
... for i in l:
... sum+=i
... return sum ...
>>> add(1,2,3)
6
>>> l = [1,2,3]
>>>add(*l)
6
for ** args,
>>> def f(**args):
... print(args)
...
>>> f()
{}
>>> f(de="Germnan",en="English",fr="French")
{'fr': 'French', 'de': 'Germnan', 'en': 'English'}
>>>
I see that it takes parameters and turns into a dictionary. But I do not understand the utility or other things that could be helpful when using ** args. In fact, I dont know what *args and **args are called(vararg and ?)
Thanks

When you use two asterisks you usually call them **kwargs for keyword arguments. They are extremely helpful for passing parameters from function to function in a large program.
A nice thing about keyword arguments is that it is really easy to modify your code. Let's say that in the below example you also decided that parameter cube is also relevant. The only thing you would need to do is add one if statement in my_func_2, and you would not need to add a parameter to every function that is calling my_func_2, (as long as that functions has **kwargs).
Here is a simple rather silly example, but I hope it helps:
def my_func_1(x, **kwargs):
if kwargs.get('plus_3'):
return my_func_2(x, **kwargs) + 3
return my_func_2(x, **kwargs)
def my_func_2(x, **kwargs):
#Imagine that the function did more work
if kwargs.get('square'):
return x ** 2
# If you decided to add cube as a parameter
# you only need to change the code here:
if kwargs.get('cube'):
return x ** 3
return x
Demo:
>>> my_func_1(5)
5
>>> my_func_1(5, square=True)
25
>>> my_func_1(5, plus_3=True, square=True)
28
>>> my_func_1(5, cube=True)
125

Related

How to specify arg position for functool partial()

As per manual, functools partial() is 'used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature.'
What's the best way to specify the positions of the arguments that one wishes to evaluate?
EDIT
Note as per comments, the function to be partially evaluated may contain named and unnamed arguments (these functions should be completely arbitrary and may be preexisting)
END EDIT
For example, consider:
def f(x,y,z):
return x + 2*y + 3*z
Then, using
from functools import partial
both
partial(f,4)(5,6)
and
partial(f,4,5)(6)
give 32.
But what if one wants to evaluate, say the third argument z or the first and third arguments x, and z?
Is there a convenient way to pass the position information to partial, using a decorator or a dict whose keys are the desired arg positions and the respective values are the arg values? eg to pass the x and z positions something like like this:
partial_dict(f,{0:4,2:6})(5)
No, partial is not designed to freeze positional arguments at non-sequential positions.
To achieve the desired behavior outlined in your question, you would have to come up with a wrapper function of your own like this:
def partial_positionals(func, positionals, **keywords):
def wrapper(*args, **kwargs):
arg = iter(args)
return func(*(positionals[i] if i in positionals else next(arg)
for i in range(len(args) + len(positionals))), **{**keywords, **kwargs})
return wrapper
so that:
def f(x, y, z):
return x + 2 * y + 3 * z
print(partial_positionals(f, {0: 4, 2: 6})(5))
outputs:
32
Simply use keyword arguments. Using your definition of f above,
>>> g = partial(f, z=10)
>>> g(2, 4)
40
>>> h = partial(f, y=4, z=10)
>>> h(2)
40
Note that once you use a keyword argument for a given parameter, you must use keyword arguments for all remaining arguments. For example, the following would not be valid:
>>> j = partial(f, x=2, z=10)
>>> j(4)
TypeError: f() got multiple values for argument 'x'
But continuing to use keyword arguments is:
>>> j = partial(f, x=2, z=10)
>>> j(y=4)
40
When you use functools.partial, you store the values of *args and **kwargs for later interpolation. When you later call the "partially applied" function, the implementation of functools.partial effectively adds the previously provided *args and **kwargs to the argument list at the front and end, respectively, as though you had inserted these argument-unpackings yourself. I.e., calling
h = partial(1, z=10)
f(4)
is roughly equivalent to writing
args = [1]
kwargs = {'z': 10}
f(*args, 4, **kwargs)
As such, the semantics of how you provide arguments to functools.partial is the same as how you would need to store arguments in the args and kwargs variables above such that the final call to f is sensible. For more information, take a look at the pseduo-implementation of functools.partial given in the functools module documentation
For easier usage, you can create a new object specifically to specify a positional argument that is to be skipped when sequentially listing values for positional arguments to be frozen with partial:
SKIP = object()
def partial_positionals(func, *positionals, **keywords):
def wrapper(*args, **kwargs):
arg = iter(args)
return func(*(*(next(arg) if i is SKIP else i for i in positionals), *arg),
**{**keywords, **kwargs})
return wrapper
so that:
def f(x, y, z):
return x + 2 * y + 3 * z
print(partial_positionals(f, 4, SKIP, 6)(5))
outputs:
32

Python 3.x: enter one list as multiple parameters in a function [duplicate]

This question already has answers here:
Pass a list to a function to act as multiple arguments [duplicate]
(3 answers)
Closed 5 years ago.
I have a list with x number of parameters
[p1,p2,p3...pk]
and I need each item on the list to pass as a parameter so it goes like
function(p1,p2..,pk)
How can I do this without changing the function to recieve a list?
something like iter items, but I cant just go next(list) on each parameter because I don't know how much parameters i'm going to insert into the function
Use * operator
list = [1, 2, 3, n]
foo(*list) # Function with n arguments
For example:
def foo(one, two):
print(one, two)
list_1 = [1, 2]
list_2 = [1, ]
# Call with list_* variable
foo(*list_1) # Print 1, 2
foo(*list_2) # TypeError, but requiere more arguments.
For solve TypeError, can use *args for has dynamic arguments in your function
Python can take arguments as *args:
def foo(*args):
print len(args)
for x in args:
print (x)
foo("a")
foo("a","b","c")
You can mix and match args and *args as well if you have a leading argument you need - something like def foo(x,y, *args):

Python, assign function to variable, change optional argument's value

Is it possible to assign a function to a variable with modified default arguments?
To make it more concrete, I'll give an example.
The following obviously doesn't work in the current form and is only meant to show what I need:
def power(a, pow=2):
ret = 1
for _ in range(pow):
ret *= a
return ret
cube = power(pow=3)
And the result of cube(5) should be 125.
functools.partial to the rescue:
Return a new partial object which when called will behave like func called with the positional arguments args and keyword arguments keywords. If more arguments are supplied to the call, they are appended to args. If additional keyword arguments are supplied, they extend and override keywords.
from functools import partial
cube = partial(power, pow=3)
Demo:
>>> from functools import partial
>>>
>>> def power(a, pow=2):
... ret = 1
... for _ in range(pow):
... ret *= a
... return ret
...
>>> cube = partial(power, pow=3)
>>>
>>> cube(5)
125
The answer using partial is good, using the standard library, but I think it's worth mentioning that the following approach is equivalent:
def cube(a):
return power(a, pow=3)
Even though this doesn't seem like assignment because there isn't a =, it is doing much the same thing (binding a name to a function object). I think this is often more legible.
In specific there's a special function for exponents:
>>> 2**3
8
But I also solved it with a lambda function, which is a nicer version of a function pointer.
# cube = power(pow=3) # original
cube = lambda x: power(x,3)

generic function in python - calling a method with unknown number of arguments

i'm new to python and need some help...
I'm implementing a generic search function that accepts an argument "fringe", which can be a data structure of many types.
in the search method I have the line:
fringe.push(item, priority)
the problem is that the push method in different data structures takes different number of arguments(some require priority and some dont). is there an ellegant way to get pass that and make the "push" method take only the number of args it requires out of the argument list sent?
Thanks!
The method to get different number of arguments and still being able of selecting the right one is the use of *args and **keyword_args parameters.
From Mark Lutz's Learning Python book:
* and **, are designed to support functions that take any number of arguments. Both can appear in either the function definition or a
function call, and they have related purposes in the two locations.
* and ** in function definition
If you define a function:
def f1(param1, *argparams, **kwparams):
print 'fixed_params -> ', param1
print 'argparams --> ', argparams
print 'kwparams ---->,', kwparams
you can call it this way:
f1('a', 'b', 'c', 'd', kw1='keyw1', kw2='keyw2')
Then you get:
fixed_params -> a
argparams --> ('b', 'c', 'd')
kwparams ---->, {'kw1': 'keyw1', 'kw2': 'keyw2'}
So that you can send/receive any number of parameters and keywords.
One typical idiom to recover keyword args is as follows:
def f1(param1, **kwparams):
my_kw1 = kwparams['kw1']
---- operate with my_kw1 ------
In this way your function can be called with any number of params and it uses those it needs.
This type or arguments are frecuently used in some GUI code like wxPython class definition and subclassing as well as for function currying, decorators, etc
* and ** in function call
* and ** params in a function call are unpacked when taken by the function:
def func(a, b, c, d):
print(a, b, c, d)
args = (2, 3)
kwargs = {'d': 4}
func(1, *args, **kwargs)
### returns ---> 1 2 3 4
Great!
In theory you could use inspect.getargspec(fringe) to find out what arguments the method takes. That will tell you the number of arguments you could pass, but it's very messy:
argspec = inspect.getargspec(fringe.push)
if len(argspec.args) >= 3 or argspec.varargs or argspec.keywords:
fringe.push(item, priority)
else:
fringe.push(item)
Much simpler just to go for it and ask forgiveness if necessary:
try:
fringe.push(item, priority)
except TypeError:
fringe.push(item)
Better still of course to make sure that the push() methods all have a consistent argument spec, but if you can't do that then use the try...except.
try below code snippets,
def push(item, priority=None):
print item,priority
args = (1,)
push(*args)
args = (1,2)
push(*args)
Can't you just use a default argument value?
>>> def foo(a, b = 10):
... print a, b
...
>>> foo(1000)
1000 10
>>> foo(1000, 1000)
1000 1000
>>>
If the argument b is not provided, it defaults to 10.

How to bind arguments to given values in Python functions? [duplicate]

This question already has answers here:
Python Argument Binders
(7 answers)
Closed 4 years ago.
I have a number of functions with a combination of positional and keyword arguments, and I would like to bind one of their arguments to a given value (which is known only after the function definition). Is there a general way of doing that?
My first attempt was:
def f(a,b,c): print a,b,c
def _bind(f, a): return lambda b,c: f(a,b,c)
bound_f = bind(f, 1)
However, for this I need to know the exact args passed to f, and cannot use a single function to bind all the functions I'm interested in (since they have different argument lists).
>>> from functools import partial
>>> def f(a, b, c):
... print a, b, c
...
>>> bound_f = partial(f, 1)
>>> bound_f(2, 3)
1 2 3
You probably want the partial function from functools.
As suggested by MattH's answer, functools.partial is the way to go.
However, your question can be read as "how can I implement partial". What your code is missing is the use of *args, **kwargs- 2 such uses, actually:
def partial(f, *args, **kwargs):
def wrapped(*args2, **kwargs2):
return f(*args, *args2, **kwargs, **kwargs2)
return wrapped
You can use partial and update_wrapper to bind arguments to given values and preserve __name__ and __doc__ of the original function:
from functools import partial, update_wrapper
def f(a, b, c):
print(a, b, c)
bound_f = update_wrapper(partial(f, 1000), f)
# This will print 'f'
print(bound_f.__name__)
# This will print 1000, 4, 5
bound_f(4, 5)

Categories

Resources