Related
We know a partial function is an original function for particular argument values. Basic syntax of partial function is,
partial(func[, *args][, **keywords])
Now, let's assume a particular program.
from functools import partial
def power(a, b):
return a ** b
pw = partial(power, b=4)
print('Default keywords for pw :', pw.keywords)
print('Default arguments for pw :', pw.args)
print('Answer of pw is: ',pw(3))
# Output
# -------
# Default keywords for pw: {'b': 4}
# Default arguments for pw: ()
# Answer of pw is: 81
The output is correct and the above partial function i set keywords as b=4 and default agrs is (). Now, if I omit the keyword b and that place i put only 4. The scenario is changed to answer is 64.
partial(power, 4)
# Output
# -------
# Default keywords for pw: {}
# Default arguments for pw: (4,)
# Answer of pw is: 64
My question is why args and keywords get interchanged when I don't want to pass b since though in the first case I didn't pass a but the result was correct.
You are providing 4 as a positional argument, and those are applied strictly from left to right when calling the partial instance. That is, partial(f, 4) is roughly the equivalent of lambda x: pow(4, x). There is no way to define an equivalent of the function lambda x: pow(x, 4) with partial without using keyword arguments.
I think you got confused with the interplay of partial and positional and keyword argument here.
Here is the simple explanations: when keywords arg. are used in the call, the order in which the arguments are listed does NOT matter! Python matches by name, not
order. The caller must supply - spam, and eggs in the examples shown next, but they can be matched by position or by name - as you can see last 2 examples.
def func(spam, eggs, toast=0, ham=1): # first 2 required
print((spam, eggs, toast, ham))
func(1, 2) # (1, 2, 0, 1)
func(1, ham=2, eggs=0) # (1, 0, 0, 2)
# *****
#
print('------------------')
print('spam-lover, hate eggs: ')
func(spam=2, eggs=0) # (2, 0, 0, 1)
print()
print('love spam and eggs: ')
func(toast=1, eggs=4, spam=2) # (2, 4, 1, 1)
print('love spam/eggs and ham, want it toast too! ')
func(ham=6, eggs=4, spam=2, toast=1) # (2, 4, 1, 6)
func(1, 2, 3, 4) # (1, 2, 3, 4)
The form - name=value means different things in the call and the def!!!
a keyword in the call and
a default in the func. header.
This question already has answers here:
What do *args and **kwargs mean? [duplicate]
(5 answers)
Closed 7 years ago.
I suppose I understand how to use them individually in functions like ...
f(*args) or f(**kargs)
But what if a function accepts both as arguments?
Ex: f(*args, **kargs)
Yes, it works
def f(*args, **kwargs):
pass
If you call function like this f(1, 3, "foo", [1, 2, 10], a=1, apple=33), then in function args will be (1, 3, "foo", [1, 2, 10]), kwargs will be{'a': 1, 'apple': 33}.
It will work too
def f(a, b, foo, *args, **kwargs):
pass
But if we call this function with the same arguments, a will be 1, b will be 3, foo will be "foo", args will be ([1, 2, 10]), kwargs will be the same.
*args gets positional arguments; **kwargs gets named arguments. For instance:
f(1, 2, 3, a=1, b=2, c=3)
would pass
args = (1, 2, 3)
kwargs = {a: 1, b: 2, c: 3}
This question already has answers here:
Python, default keyword arguments after variable length positional arguments
(2 answers)
Closed 9 years ago.
I am trying to figure out if I can leave an optional argument out (use it's default value) when using *args in Python. The following code works through "print(a)", so explicitly including the optional argument h in the deriv function call works. Can I leave it (h) out somehow? My attempts ("b = ...", "c = ...", "d = ...") fail. Is there another way?
def deriv(f, x, h=1.e-9, *params):
return (f(x+h, *params)-f(x-h, *params))/(2.*h)
def f1(x, a, p):
return a*x**p
a = deriv(f1, 3, 1.e-9, 4, 5)
print(a)
b = deriv(f1, 3, , 4, 5)
c = deriv(f1, 3, 4, 5)
d = deriv(f1, 3, h, 4, 5)
No, python applies positional arguments to all named arguments first; h is the third positional argument in the function signature and thus only argument positions 4 and over are captured by *params.
Instead, use a **kwargs argument catching arbitrary keyword arguments and look h up in that:
def deriv(f, x, *params, **kwargs):
h = kwargs.pop('h', 1.e-9)
You'll now have to name h explicitly when calling deriv:
b = deriv(f1, 3, 4, 5, h=2.0)
It looks like you're using Python 3, so you can use keyword-only arguments:
>>> def deriv(f, x, *params, h=1.0E-9):
print(f)
print(x)
print(params)
print(h)
>>> deriv(pow, 'x', 10, 20, 30)
<built-in function pow>
x
(10, 20, 30)
1e-09
>>> deriv(pow, 'x', 10, 20, 30, h=.2)
<built-in function pow>
x
(10, 20, 30)
0.2
Take for example the python built in pow() function.
xs = [1,2,3,4,5,6,7,8]
from functools import partial
list(map(partial(pow,2),xs))
>>> [2, 4, 8, 16, 32, 128, 256]
but how would I raise the xs to the power of 2?
to get [1, 4, 9, 16, 25, 49, 64]
list(map(partial(pow,y=2),xs))
TypeError: pow() takes no keyword arguments
I know list comprehensions would be easier.
No
According to the documentation, partial cannot do this (emphasis my own):
partial.args
The leftmost positional arguments that will be prepended to the positional arguments
You could always just "fix" pow to have keyword args:
_pow = pow
pow = lambda x, y: _pow(x, y)
I think I'd just use this simple one-liner:
import itertools
print list(itertools.imap(pow, [1, 2, 3], itertools.repeat(2)))
Update:
I also came up with a funnier than useful solution. It's a beautiful syntactic sugar, profiting from the fact that the ... literal means Ellipsis in Python3. It's a modified version of partial, allowing to omit some positional arguments between the leftmost and rightmost ones. The only drawback is that you can't pass anymore Ellipsis as argument.
import itertools
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(newfunc.leftmost_args + fargs + newfunc.rightmost_args), **newkeywords)
newfunc.func = func
args = iter(args)
newfunc.leftmost_args = tuple(itertools.takewhile(lambda v: v != Ellipsis, args))
newfunc.rightmost_args = tuple(args)
newfunc.keywords = keywords
return newfunc
>>> print partial(pow, ..., 2, 3)(5) # (5^2)%3
1
>>> print partial(pow, 2, ..., 3)(5) # (2^5)%3
2
>>> print partial(pow, 2, 3, ...)(5) # (2^3)%5
3
>>> print partial(pow, 2, 3)(5) # (2^3)%5
3
So the the solution for the original question would be with this version of partial list(map(partial(pow, ..., 2),xs))
Why not just create a quick lambda function which reorders the args and partial that
partial(lambda p, x: pow(x, p), 2)
You could create a helper function for this:
from functools import wraps
def foo(a, b, c, d, e):
print('foo(a={}, b={}, c={}, d={}, e={})'.format(a, b, c, d, e))
def partial_at(func, index, value):
#wraps(func)
def result(*rest, **kwargs):
args = []
args.extend(rest[:index])
args.append(value)
args.extend(rest[index:])
return func(*args, **kwargs)
return result
if __name__ == '__main__':
bar = partial_at(foo, 2, 'C')
bar('A', 'B', 'D', 'E')
# Prints: foo(a=A, b=B, c=C, d=D, e=E)
Disclaimer: I haven't tested this with keyword arguments so it might blow up because of them somehow. Also I'm not sure if this is what #wraps should be used for but it seemed right -ish.
you could use a closure
xs = [1,2,3,4,5,6,7,8]
def closure(method, param):
def t(x):
return method(x, param)
return t
f = closure(pow, 2)
f(10)
f = closure(pow, 3)
f(10)
You can do this with lambda, which is more flexible than functools.partial():
pow_two = lambda base: pow(base, 2)
print(pow_two(3)) # 9
More generally:
def bind_skip_first(func, *args, **kwargs):
return lambda first: func(first, *args, **kwargs)
pow_two = bind_skip_first(pow, 2)
print(pow_two(3)) # 9
One down-side of lambda is that some libraries are not able to serialize it.
One way of doing it would be:
def testfunc1(xs):
from functools import partial
def mypow(x,y): return x ** y
return list(map(partial(mypow,y=2),xs))
but this involves re-defining the pow function.
if the use of partial was not 'needed' then a simple lambda would do the trick
def testfunc2(xs):
return list(map(lambda x: pow(x,2), xs))
And a specific way to map the pow of 2 would be
def testfunc5(xs):
from operator import mul
return list(map(mul,xs,xs))
but none of these fully address the problem directly of partial applicaton in relation to keyword arguments
Even though this question was already answered, you can get the results you're looking for with a recipe taken from itertools.repeat:
from itertools import repeat
xs = list(range(1, 9)) # [1, 2, 3, 4, 5, 6, 7, 8]
xs_pow_2 = list(map(pow, xs, repeat(2))) # [1, 4, 9, 16, 25, 36, 49, 64]
Hopefully this helps someone.
Yes, you can do it, provided the function takes keyword arguments. You just need to know the name.
In the case of pow() (provided you are using Python 3.8 or newer) you need exp instead of y.
Try to do:
xs = [1,2,3,4,5,6,7,8]
print(list(map(partial(pow,exp=2),xs)))
As already said that's a limitation of functools.partial if the function you want to partial doesn't accept keyword arguments.
If you don't mind using an external library 1 you could use iteration_utilities.partial which has a partial that supports placeholders:
>>> from iteration_utilities import partial
>>> square = partial(pow, partial._, 2) # the partial._ attribute represents a placeholder
>>> list(map(square, xs))
[1, 4, 9, 16, 25, 36, 49, 64]
1 Disclaimer: I'm the author of the iteration_utilities library (installation instructions can be found in the documentation in case you're interested).
The very versatile funcy includes an rpartial function that exactly addresses this problem.
xs = [1,2,3,4,5,6,7,8]
from funcy import rpartial
list(map(rpartial(pow, 2), xs))
# [1, 4, 9, 16, 25, 36, 49, 64]
It's just a lambda under the hood:
def rpartial(func, *args):
"""Partially applies last arguments."""
return lambda *a: func(*(a + args))
If you can't use lambda functions, you can also write a simple wrapper function that reorders the arguments.
def _pow(y, x):
return pow(x, y)
and then call
list(map(partial(_pow,2),xs))
>>> [1, 4, 9, 16, 25, 36, 49, 64]
Yes
if you created your partial class
class MyPartial:
def __init__(self, func, *args):
self._func = func
self._args = args
def __call__(self, *args):
return self._func(*args, *self._args) # swap ordering
xs = [1,2,3,4,5,6,7,8]
list(map(MyPartial(pow,2),xs))
>>> [1, 4, 9, 16, 25, 36, 49, 64]
I am currently learning python coding and I come across this qns on a learning site:
Create a function that takes a sequence of inputs (may be a list, a tuple, or just a bunch of inputs). The function should return the minimum and the maximum of the list.
This are some of the test values that are using:
minmax(5,4)
4,5
minmax(5,4,8,3,5,7,4)
3,8
minmax([5,4,6])
4,6
minmax(5.1,4.2,68.34,129.1,-90.3)
-90.3,129.1
And I had tried doing it this way but when the parameter is a list, I can't seems to convert it into a tuple and find the max and min.
Here is what I had tried:
def minmax(*a):
b = tuple(a)
minNum = min(b)
maxNum = max(b)
c = (minNum, maxNum)
return c
When a list is taken in, the return result is ([5, 4, 6], [5, 4, 6])
def minmax(*a):
if len(a) == 1: # Single (list, tuple, or scalar) argument
try:
return minmax(*a[0]) # Expansion of sequence elements, if possible
except TypeError: # Case of minmax(42)
pass # The general code below handles this directly
return (min(a), max(a))
>>> minmax(3, 5, 1, 10)
(1, 10)
>>> minmax([3, 5, 1, 10])
(1, 10)
>>> minmax((42, 123, -12))
(-12, 123)
>>> minmax(42)
42
This works in more cases than the built-in min() and max(), which do not work on a single scalar argument (min(42)).
>>> min(42)
TypeError: 'int' object is not iterable
It is however possible to write a simpler version that behaves like the built-in min() and max() (see my other answer, for instance).
This works by forcing min() to be given strictly more than 1 element, except in the special case of minmax(42), which calls min((42,)).
To be able to handle different ways of passing in arguments, you need to have some conditions to handle each case.
>>> def minmax(*a):
... if len(a) == 1 and hasattr(a[0], '__getitem__'):
... # handle a single sequence passed in
... return min(a[0]), max(a[0])
... # handle values passed in
... return min(a), max(a)
...
>>> minmax(5, 4)
(4, 5)
>>> minmax(5, 4, 8, 3, 5, 7, 4)
(3, 8)
>>> minmax([5, 4, 6])
(4, 6)
>>> minmax(5.1, 4.2, 68.34, 129.1, -90.3)
(-90.3, 129.1)
A simpler but slightly less powerful solution that mirrors my other solution is:
def minmax(*a):
if len(a) == 1: # Single (list or tuple) argument
return (min(a[0]), max(a[0]))
return minmax(a) # Single tuple argument given to minmax()
This version forces min() to be given a single (list or tuple) argument.
It behaves like the built-in min() and max(): min(42) and minmax(42) both raise an exception:
>>> minmax(3, 5, 1, 10)
(1, 10)
>>> minmax([3, 5, 1, 10])
(1, 10)
>>> minmax((42, 123, -12))
(-12, 123)
>>> minmax(42)
TypeError: 'int' object is not iterable
>>> min(42)
TypeError: 'int' object is not iterable