Send different number of arguments to a function in a Pythonic way [duplicate] - python

This question already has answers here:
Can a variable number of arguments be passed to a function?
(6 answers)
Closed 3 months ago.
I'm trying to make a small function which calls another function from a library I import, I have 8 similar use cases but I don't want the code to be long and repeating.
each time I send the exact same function and with the same arguments but with different number of them.
Let me show an example of what I mean:
This is my function
def num_pack(num, 8_bytes):
return struct.Struct(">Q Q Q Q Q Q Q Q").pack(num, num, num, num, num, num, num, num)
num is some generic counter, 8_bytes is a variable that runs from 1 to 8.
there are 8 possible options for the function that I use, it depends on the 8_bytes value.
The number of Q in the string should be equal to the number of 8_bytes and the same goes for num.
The naive way to do it is :
def num_pack(num, 8_bytes):
if 8_bytes == 8:
return struct.Struct(">Q Q Q Q Q Q Q Q").pack(num, num, num, num, num, num, num, num)
if 8_bytes == 7:
return struct.Struct(">Q Q Q Q Q Q Q").pack(num, num, num, num, num, num, num)
if 8_bytes == 6:
return struct.Struct(">Q Q Q Q Q Q").pack(num, num, num, num, num, num)
.
.
.
if 8_bytes == 1:
return struct.Struct(">Q").pack(num)
I know how to modify the ">Q" string at each time by I don't know how to change the pack function's number of arguments.
I also know how to do this with eval, but this is bad practice and I don't want to use this method.
I'm sure there is some Pythonic way of doing so,
Thanks in advance !

You can create a function that accepts any amount of arguments using the unpack operater *.
Here is an example of a function that takes any amount of arguments:
def my_awesome_function(*args):
# do awesome job!
print(args)
# depending on len of args do something...
Or if you always have a one argument, but an unknown number of following arguments, you can formulate it like this:
def my_awesome_function(num, *all_other_args):
# my number
print(num)
# do awesome job!
print(all_other_args)
# depending on len of args do something...
Here is full explanation of how to deal with unknown number of args, and more usefull operators: https://www.scaler.com/topics/python/packing-and-unpacking-in-python/
If you are looking to do it the other way around, such that a function deeds all the items in a list as separate arguments. This could be done like this with the * operator as well.
def foo(a, b, c):
print(a, b, c)
values = ['adam', 'dave', 'elon']
# a valid funciton call would be
foo(*values)
# values -> ['adam', 'dave', 'elon']
# *values -> 'adam', 'dave', 'elon'
Hopefully this helps. Please leave a comment if I misunderstood.
Good Luck!

Related

What is the point of using *args when a list of arguments can be used?

Would passing in a list or dictionary of variables be more concise than passing in *args in Python methods?
For example,
def function(a, *argv):
print('First variable:', a)
for k in argv:
print('Additional variable:',k)
is the same as
def function(a, list):
print('First variable:', a)
for k in list:
print('Additional variable:',k)
except a list is passed in the second argument. What I think using *args would often do is to cause additional bugs in the program because the argument length only needs to be longer than the mandatory argument length. Would any please explain situations where *args would be really helpful? Thanks
The first function accepts:
function('hello', 'I', 'am', 'a', 'function')
The second one won't. For the second you'd need:
function('hello', ['I', 'am', 'a', 'function'])
In principle, the first one is used when your function can have an arbitrary number of parameters (think: print), while the second one specifies that there's always a second parameter, which is an iterable (not necessarily a list, despite the name)
Passing *args is useful when you have to extract only some (or none) arguments in first level function and then pass others to other inner function without knowing about the details. e.g.
def inner_func(x, y, z, *args, **kwargs):
# do something with x, y, and z
return output
def outer_func(a, b, *args, **kwargs):
# do something with a and b
# pass the rest arguments to inner function without caring about details
output = inner_func(*args, **kwargs)
# do something with output
return
That is a fair ask as to why *args (or **kwargs) is essentially required when a list (or dict) could do the same task. The key reason to that is when a ** caller of a function does not know the number of arguments beforehand**. I'll try to explain this with reference to the particular scenario you have shared.
Lets suppose that we have the below function which finds the sum of all integers passed in. (I'm giving up sum builtin function for demonstration purpose, please bear with me :) )
def do_add(*int_args):
total = 0
for num in int_args:
total += num
return total
And you want to call this for an unknown number of arguments with an unknown number of times.
If in case you need to send a list argument, the do_add function might look like below:
def do_add(int_list):
total = 0
for num in int_list:
total += 0
return total
l1 = [1, 2, 3, 4, ... n] # memory loaded with n int objects
do_add(l1)
l2 = [10, 20, ... n]
do_add(l2)
Firstly, you are loading the memory with an additional list object created just for the sake of function call. Secondly, if you have to add some more items to the list we may need to call another list method such as append or extend.
But if you follow the *args approach, you can avoid creating an extra list and focus only on the function call. If you need to add more arguments you can just add another argument separated by a comma rather than calling append or extend methods.
Assume that you want to call this function n times with 1000 arguments. It will result in n * 1000 new list objects to be created every time. But with the variable arguments approach, you can just call it directly.
do_add(1, 2, 3) # call 1
do_add(10.0, 20.0, 30.0) # call 2
...
do_add(x, y, z, ..., m) # call n

Switch/Case in Matlab to If/Else Python

I am working on translating some code from Matlab into Python. Currently I am working on this:
switch SIGNATURES_TYPE
case 1
rand('seed',5);
load('USGS_1995_Library')
wavlen=datalib(:,1); % Wavelengths in microns
[L n_materiais]=size(datalib);
# select randomly
sel_mat = 4+randperm(n_materiais-4);
sel_mat = sel_mat(1:p);
M = datalib(:,sel_mat);
# print selected endmembers
clear datalib wavelen names aux st;
case 2
error('type not available')
case 3
M = rand(L,p);
case 4
M = randn(L,p);
case 5
L=p;
M = diag(linspace(1,(1/(COND_NUMBER)^(1/DECAY)),p).^DECAY);
case 6
L=p;
M = diag(linspace(1,(1/(COND_NUMBER)^(1/DECAY)),p).^DECAY);
A = randn(p);
[U,D,V] = svd(A);
M = U*M*V';
clear A U D V;
otherwise
error('wrong signatute type')
end
Previously I had worked on a similar Switch/Case code:
for i=1:2:(length(varargin)-1)
switch upper(varargin{i})
case 'MM_ITERS'
MMiters = varargin{i+1};
case 'SPHERIZE'
spherize = varargin{i+1};
The latter I was able to translate to this:
for i in range(1, 2, length(*args)-1):
if (arg[i].upper() == "MM_ITERS"):
MMiters = arg(i+1)
elif (arg[i].upper() == "SPHERIZE"):
spherize = arg(i+1)
However for the former I am wondering how I can create similar if statements. For example, for the first case can my code be something like:
if SIGNATURES_TYPE == 0:
** finish function
I wanted to know if something like this works or if it would be better to perhaps separate out statements into separate functions and then call them?
Thanks for the help and input!
The if/else construct would work. Another way to do this is to use a dict where the keys are the signature types and the values are functions that return M. This has the advantage of not having to clear anything. So it would look something like this:
def case1(p):
# calculate L and M
return L, M
def case6(p):
# calculate M
return p, M
try:
L, M = {1: case1(p),
3: (L, np.random.rand(L, p)),
4: (L, np.random.randn(L,p)),
5: (p, np.diag(np.linspace(1,(1/(COND_NUMBER)**(1/DECAY)),p)**DECAY),
6: case6(p)}[SIGNATURES_TYPE]
except KeyError:
raise ValueError('Wrong signatute type: {}'.format(SIGNATURES_TYPE))
As for your previous example, the MATLAB code is a workaround for the lack of default argument handling. Python has default argument handling, so you don't need the if/else or loop at all. You can just do something like:
def myfunc(arg1, arg2, arg3, MM_ITERS=MM_ITERS_default, SPHERIZE=SPHERIZE_default):
where arg1, arg2, and arg3 are the required arguments (the number doesn't matter, there could even be zero). MM_ITERS_default and SPHERIZE_default are the values you want the corresponding variables to hold when the user doesn't define them. You can even collect the arguments directly into a dict, using:
def myfunc(arg1, arg2, arg3, **kwargs):
Then you can access, say MM_ITERS, just using something like:
if MM_ITERS in kwargs:
MM_ITERS = kwargs[MM_ITERS]`
Or
MM_ITERS = kwargs.get(MM_ITERS, MM_ITERS_default)
But generally it is easier to just use the default argument handling I showed earlier.

Most pythonic way to write a function to either pass in arguments or a tuple of arguments

What is a most pythonic way to write a function to either pass in arguments or a tuple/list of arguments?
For example, a function add could either take in an argument of add(1, 2) or add((1, 2)) and both output 3.
What I have so far: (it works, but does not look nice)
def add(*args):
if len(args) == 1:
return (args[0][0] + args[0][1])
if len(args) == 2:
return args[0] + args[1]
else:
print "error: add takes in one or two arguments"
What I don't like about it is:
I have to print the error about passing in one or two arguments
The args[0][0] looks very unreadable
This way, it is hard to tell what the arguments passed in represent (they don't have names)
I dont know if this is the most "pythonic" way but it will do what you want:
def add(a, b=None):
return a+b if b is not None else sum(a)
If your function takes a specific number of arguments, then the most pythonic way to do this is to not do it. Rather if the user has a tuple with the arguments, you make them unpack them when they call the function. E.g.
def add(a, b):
return a + b
Then the caller can do
add(1,2)
or
t = (1,2)
add(*t)
The only time you want to accept either a sequence of params or individual params is when you can have any arbitrary (non-zero) number of arguments (e.g. the max and min builtin functions) in which case you'd just use *args
If you can only take a finite number of arguments, it makes more sense to ask for those specifically. If you can accept an arbitrary number of arguments, then the *args paradigm works well if you loop through it. Mixing and matching those two aren't very elegant.
def add(*args):
total = 0
for i in args:
total += i
return total
>>> add(1, 2, 3)
6
(I know we could just use sum() there, but I'm trying to make it look a bit more general)
In the spirit of python duck typing, if you see 1 argument, assume its something that expands to 2 arguments. If its then 2, assume its two things that add together. If it violates your rules, raise an exception like python would do on a function call.
def add(*args):
if len(args) == 1:
args = args[0]
if len(args) != 2:
raise TypeError("add takes 2 arguments or a tuple of 2 arguments")
return args[0] + args[1]
A decorator would be best suited for this job.
from functools import wraps
def tupled_arguments(f):
#wraps(f) # keeps name, docstring etc. of f
def accepts_tuple(tup, *args):
if not args: # only one argument given
return f(*tup)
return f(tup, *args)
return accepts_tuple
#tupled_arguments
def add(a, b):
return a + b

calculating current value based on previous value

i would like to perform a calculation using python, where the current value (i) of the equation is based on the previous value of the equation (i-1), which is really easy to do in a spreadsheet but i would rather learn to code it
i have noticed that there is loads of information on finding the previous value from a list, but i don't have a list i need to create it! my equation is shown below.
h=(2*b)-h[i-1]
can anyone give me tell me a method to do this ?
i tried this sort of thing, but that will not work as when i try to do the equation i'm calling a value i haven't created yet, if i set h=0 then i get an error that i am out of index range
i = 1
for i in range(1, len(b)):
h=[]
h=(2*b)-h[i-1]
x+=1
h = [b[0]]
for val in b[1:]:
h.append(2 * val - h[-1]) # As you add to h, you keep up with its tail
for large b list (brr, one-letter identifier), to avoid creating large slice
from itertools import islice # For big list it will keep code less wasteful
for val in islice(b, 1, None):
....
As pointed out by #pad, you simply need to handle the base case of receiving the first sample.
However, your equation makes no use of i other than to retrieve the previous result. It's looking more like a running filter than something which needs to maintain a list of past values (with an array which might never stop growing).
If that is the case, and you only ever want the most recent value,then you might want to go with a generator instead.
def gen():
def eqn(b):
eqn.h = 2*b - eqn.h
return eqn.h
eqn.h = 0
return eqn
And then use thus
>>> f = gen()
>>> f(2)
4
>>> f(3)
2
>>> f(2)
0
>>>
The same effect could be acheived with a true generator using yield and send.
First of, do you need all the intermediate values? That is, do you want a list h from 0 to i? Or do you just want h[i]?
If you just need the i-th value you could us recursion:
def get_h(i):
if i>0:
return (2*b) - get_h(i-1)
else:
return h_0
But be aware that this will not work for large i, as it will exceed the maximum recursion depth. (Thanks for pointing this out kdopen) In that case a simple for-loop or a generator is better.
Even better is to use a (mathematically) closed form of the equation (for your example that is possible, it might not be in other cases):
def get_h(i):
if i%2 == 0:
return h_0
else:
return (2*b)-h_0
In both cases h_0 is the initial value that you start out with.
h = []
for i in range(len(b)):
if i>0:
h.append(2*b - h[i-1])
else:
# handle i=0 case here
You are successively applying a function (equation) to the result of a previous application of that function - the process needs a seed to start it. Your result looks like this [seed, f(seed), f(f(seed)), f(f(f(seed)), ...]. This concept is function composition. You can create a generalized function that will do this for any sequence of functions, in Python functions are first class objects and can be passed around just like any other object. If you need to preserve the intermediate results use a generator.
def composition(functions, x):
""" yields f(x), f(f(x)), f(f(f(x)) ....
for each f in functions
functions is an iterable of callables taking one argument
"""
for f in functions:
x = f(x)
yield x
Your specs require a seed and a constant,
seed = 0
b = 10
The equation/function,
def f(x, b = b):
return 2*b - x
f is applied b times.
functions = [f]*b
Usage
print list(composition(functions, seed))
If the intermediate results are not needed composition can be redefined as
def composition(functions, x):
""" Returns f(x), g(f(x)), h(g(f(x)) ....
for each function in functions
functions is an iterable of callables taking one argument
"""
for f in functions:
x = f(x)
return x
print composition(functions, seed)
Or more generally, with no limitations on call signature:
def compose(funcs):
'''Return a callable composed of successive application of functions
funcs is an iterable producing callables
for [f, g, h] returns f(g(h(*args, **kwargs)))
'''
def outer(f, g):
def inner(*args, **kwargs):
return f(g(*args, **kwargs))
return inner
return reduce(outer, funcs)
def plus2(x):
return x + 2
def times2(x):
return x * 2
def mod16(x):
return x % 16
funcs = (mod16, plus2, times2)
eq = compose(funcs) # mod16(plus2(times2(x)))
print eq(15)
While the process definition appears to be recursive, I resisted the temptation so I could stay out of maximum recursion depth hades.
I got curious, searched SO for function composition and, of course, there are numerous relavent Q&A's.

Python calling a function inside a function

I'm a beginner to CS, and I've been trying to work through a Python book (Think Python) on my own.
I'm currently on recursion, but I'm a bit stuck.
The exercise asks for me to write a function called do_n that takes a function object and a number, n, as arguments, and that calls the given function n times.
This is my code:
def countdown(n):
if n<= 0:
print 'Blastoff'
return
else:
print n
countdown(n-1)
def do_n(f(n), x):
if x<=0:
return
else:
f(n)
do_n(f, x-1)
do_n(countdown(3), 3)
When I do this, there's an error because of invalid syntax in def do_n(f(n), x). If I change it to:
def do_n(f, x):
if x<=0:
return
else:
f(n)
do_n(f, x-1)
There is an error because n is not defined in the else statement.
How can I make this work?
You are almost there with your second example. It takes a function f and a max count x. n doesn't exist because you haven't written the loop to generate values for n yet. Turns out python has a builtin for that
def do_n(f, x):
for n in range(x):
f(n)
Now do_n takes a function object f and a count x, then calls the function count times. Notice the difference between f (the function object) and f(n) (the result of calling f with the value n). When you want to call do_n, you do it like this:
do_n(countdown, 3)
Not like
do_n(countdown(3), 3) # badness
That last one calls countdown and then calls do_n with its result.
def print_n(s,n):
if n<=0:
return
else:
print(s)
print_n(s,n-1)
def do_n(f,s,n,x):
if x<=0:
return
else:
f(s,n)
do_n(f,s,n,x-1)
do_n(print_n,'Hello',2,2)
It's a tricky one.
Basically, the solution is:
def do_n(f, n):
if n <= 0:
return
f(n)
do_n(f, n-1)
However, if you actually try combining it with print_n then all hell breaks loose.
First of all, we have to add one more argument, s, to the above code to be able to pass any string that we want to print out n times.
Second of all, the above do_n should be able to repeat any passed function n times so long as the function doesn't mess with do_n. Unfortunately, this is exactly what happens here.
The assignment from the book seems clear: "...write a function called do_n that takes a function object and a number, n, as arguments, and that calls the given function n times..." or in other words do_n calls any function passed to it n times. You'd think if we pass s = 'Hello' and n = 3 then do_n(f, s, n) should output 'Hello' 9 times... because print_n('Hello', 3) prints 'Hello' 3 times and do_n(print_n, 'Hello', 3) is supposed to triple that result... gotcha.
What actually happens is in the first instance of recursion do_n does indeed print out 'Hello' 3 times, because print_n says so. But in the next instance do_n prints out 'Hello' only two times, because it has subtracted 3-1 in the previous instance and now print_n('Hello', 2). In the last instance do_n prints 'Hello' only once, for the same reason. So the total amount of 'Hello' output is 6.
Finally, if you use FOR LOOP instead of recursion to define do_n, you will actually get the correct result of 9 outputs:
def print_n(s, n):
if n <= 0:
return
print(s)
print_n(s, n-1)
def do_n(f, s, n):
for i in range(n):
f(s, n)
do_n(print_n, 'Hello', 3)
P.S.
On Page 52 of Think Python 2E we are encouraged to use recursion over for loop:
"For simple examples like this, it is probably easier to use a for loop. But we will see examples later that are hard to write with a for loop and easy to write with recursion, so it is good to start early."
However, in this particular exercise, using recursion forces an output which clashes with the assignment, seeing that do_n and print_n are at odds with each other because constructing do_n with the help of recursion wrongly reduces n in each instance.
Did I get it right?
thinkpython2e
recursion

Categories