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.
Related
Currently, I have a function like so:
def my_func(*args):
#prints amount of arguments
print(len(args))
#prints each argument
for arg in args:
print(arg)
I want to put multiple arguments through to this function, but the following doesn't work for me. It gives out a syntax error on the asterisk * after the else.
my_func(
*(1, 2, 3, 4)
if someBool is True
else *(1, 2)
)
The workaround I found puts 1 and 2 in first and then puts 3 and 4 while checking for someBool.
my_func(
1, 2,
3 if someBool is True else None,
4 if someBool is True else None
)
I am fine with the above as my function checks for None but if there is an alternative I would gladly thank them.
Move the * to outside the ... if ... else ...:
my_func(
*((1, 2, 3, 4)
if someBool is True
else (1, 2))
)
You need an extra set of parenthesis. Also, you don't need to say is True to check if a Boolean is "truthy" in python, making it: my_func(*((1, 2, 3, 4) if someBool else (1, 2))).
I have a function that accepts *args, but I would like to set a default tuple, in case none are provided. (This is not possible through def f(*args=(1, 3, 5)), which raises a SyntaxError.) What would be the best way to accomplish this? The intended functionality is shown below.
f()
# I received 1, 2, 3!
f(1)
# I received 1!
f(9, 3, 72)
# I received 9, 3, 72!
The following function g will provide the correct functionality, but I would prefer *args.
def g(args=(1, 2, 3)):
return "I received {}!".format(', '.join(str(arg) for arg in args))
g()
# I received 1, 2, 3!
g((1,))
# I received 1!
g((9, 3, 72))
# I received 9, 3, 72!
You could check whether args are truthy in your function:
def g(*args):
if not args:
args = (1, 2, 3)
return "I received {}!".format(', '.join(str(arg) for arg in args))
If no args are passed to the function, it will result in a empty tuple, which evaluates to False.
If no arguments are received, args will be an empty tuple. You can't add a default value in the method signature itself, but you can check if args is empty and replace it with a fallback value inside the function.
def g(*args):
if not args:
args = (1, 2, 3)
return 'I received {}!'.format(', '.join(str(arg) for arg in args))
def g(*args):
if not args:
args = [1, 3, 5]
return "I received {}!".format(', '.join(str(arg) for arg in args))
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
I wonder, is there a function in python -let's call it now apply- that does the following:
apply(f_1, 1) = f_1(1)
apply(f_2, (1, 2)) = f_1(1, 2)
...
apply(f_n, (1, 2,..., n)) = f_n(1, 2,..., n) # works with a tuple of proper length
Since it does exist in eg. A+ and Mathematica and it used to be really useful for me.
Cheers!
Python has language-level features for this, known as "argument unpacking", or just "splat".
# With positional arguments
args = (1, 2, 3)
f_1(*args)
# With keyword arguments
kwargs = {'first': 1, 'second': 2}
f_2(**kwargs)
You can use the * operator for the same effect:
f_1(*(1, 2)) = f_1(1, 2)
...
The expression following the * needn't be a tuple, it can be any expression that evaluates to a sequence.
Python also has a built-in apply function that does what you'd expect, but it's been obsolete in favor of the * operator since Python 2.3. If you need apply for some reason and want to avoid the taint of deprecation, it is trivial to implement one:
def my_apply(f, args):
return f(*args)
Yep, use the * operator on the list of arguments. For a practical example:
max(1, 2, 3, 4, 5) # normal invocation
=> 5
max(*[1, 2, 3, 4, 5]) # apply-like invocation
=> 5
Think of the second snippet as equivalent to apply(max, [1, 2, 3, 4, 5])
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