Gather arguments in a recursive function - python

I'm currently learning Python, and I have problems modifying this function to work as I would like. This is a recursive function that finds gcd from two integers. data is a tuple with two integers. How to modify this recursive function to work with one parameter?
def gcd(data):
a, b = data
if b == 0: return a
return gcd(b, a % b)
If i execute it like this, i get
TypeError: checkio() takes 1 positional argument but 2 were given
If I try to gather arguments by defining def gcd(*data):, I get
ValueError: need more than 1 value to unpack.
Is this possible at all ?

Your function is expecting a single variable (a tuple), but you are passing two variables, try this:
return gcd((b, a % b))

Sure, you just need to be consistent. Either define gcd to take two parameters:
def gcd(a, b):
or wrap the values in a tuple when you call it:
return gcd((b, a % b))

I think you should just be able to put an extra pair of parentheses around your arguments, like so:
return gcd((b, a % b))
This will make your two positional arguments into a tuple passed as a single positional argument.
Alternatively you could drop the unpacking and just accept two positional arguments instead of a single tuple in the first place.

Passing a literal tuple as a parameter requires another set of parentheses to make clear that it is not 2 separate parameters:
return gcd((b, a % b))
Alternatively, the tuple could be assigned to a separate name first (with or without parentheses):
next_data = b, a % b
return gcd(next_data)
gcd(*data) would work, but you would also need to change your initial call to pass individual parameters as well, in the form of either gcd(a, b) or gcd(*data).

Related

i want to return two values from one lambda and assign to the other one but i got error

I had tried to return values to a and b by using the below method
(lambda a,b:print(a,b))((lambda x:(x,[int(i)**len(x) for i in x]))('153'))
but this shows error,i need some help to fix this.
TypeError: <lambda>() missing 1 required positional argument: 'b'
The inner function returns a single tuple of two values, but the outer function expects two separate values. Use *-unpacking to have each value of the tuple passed as a separate parameter:
# v takes two parameters v provides one tuple of two values
(lambda a,b:print(a,b))(*(lambda x:(x,[int(i)**len(x) for i in x]))('153'))
# ^ unpack operator
Note that print already takes positional arguments – (lambda a,b:print(a,b)) can be replaced by just print. Also, Python3.8 introduces the := assignment operator, which can often be used instead of a lambda to emulate let expressions. This shortens the expression significantly:
# v print takes multiple arguments
print(*(x := '153', [int(i)**len(x) for i in x]))
# ^ assignment operator binds in current scope
#MisterMiyagi posted the correct answer using the given structure. However, I can't think of a case where using two lambdas in the way you did would be useful. Defining a function would make the code much more readable:
def print_values(string):
values = [int(i)**len(string) for i in string]
print(string, values)
print_values("153")
Or if you want it shorter:
def print_values(string):
print(string, [int(i)**len(string) for i in string])
print_values("153")

How to pass tuple and variable as args

I need to pass a large tuple and a single variable into a threaded task as arguments.
excelbtn_text.set("Outputting...")
excelClass = excelCL()
excel_thread = threading.Thread(target=excelClass.excelOut, args=(dcf_data_tuple, excelbtn_text))
excel_thread.daemon = True
excel_thread.start()
However I receive an error, TypeError: excelOut() missing 242 required positional arguments. Is there anyway I can get past this problem?
The error never occurred while I was only passing the tuple as an argument.
First, to pass a tuple and another value, you can just make another tuple with two members—the big tuple, and the other value. Exactly as you're doing:
args=(dcf_data_tuple, excelbtn_text)
But the arguments you pass have to match the method's function definition. Passing a valid tuple of 2 values as the arguments for a method doesn't work unless that method takes 2 parameters.
To resolve your confusion, first, this does not mean what you think it does:
args=(dcf_data_tuple)
Parentheses do not create a tuple; commas create a tuple. In other words, (2) is not a 1-element tuple containing the number 2, it's just the number 2. And (dct_data_tuple) is not a 1-element tuple containing the tuple dct_data_tuple, it's just dct_data_tuple.
So, the function definition for excelClass.excelOut is presumably taking not a single giant tuple as a parameter, but rather hundreds of separate parameters.
This is a bizarre design, but it's not actually illegal.
And that matches the exception you're getting: when you pass it 2 arguments (the first of which is a giant tuple), rather than hundreds arguments, it complains that you're missing 242 positional arguments:
TypeError: excelOut() missing 242 required positional arguments
The simplest way to fix this is to give excelOut a reasonable signature that matches what you want to pass it:
def excelOut(self, data_tuple, text):
# do stuff here
If you for some reason can't change its definition, then you have to look at what the definition is, and try to match it. If, for example, it looks like this:
def excelOut(self, data0, data1, … hundreds more, …, text):
… then you have to call it like this:
args=dcf_data_tuple + (excelbtn_text,)
Notice the comma at the end. That means (excelbtn_text,) is a 1-element tuple. And then we add the giant tuple to the 1-element tuple and get back a giant-plus-1-element tuple, which now matches the method's parameters.
If I understood your problem, you can add an asterisk before your tuple to pass the arguments unwrapped:
excelbtn_text.set("Outputting...")
excelClass = excelCL()
excel_thread = threading.Thread(target=excelClass.excelOut, args=(*dcf_data_tuple, excelbtn_text))
excel_thread.daemon = True
excel_thread.start()

Python Return Multiple Values so it can be used in another function

The easiest way to show this is an example:
def hat(a, b):
return a+b
def cat(c, d):
return c % 2, d % 2
If I type cat(4,5) I properly get (0,1). However if I try hat(cat(4,5)) instead of getting 1, I get an error saying hat needs more values. In my real function I am dealing with a lot more than 2 arguments, so what is the proper way to fix this
The cat function actually returns a tuple, when used as an argument to hat, it can only match the tuple against the first argument a. You need to expand the tuple into multiple arguments.
Try a call like:
hat(*cat(4,5))
The * will expand the tuple and bind against all the arguments.

How to understand variable-length argument in python function?

I want to write my own sum function to get the sum of a variable-length argument.
def myadd(*tuple):
sum=0
for element in tuple:
sum=sum+element
return(sum)
call method 1:
myadd(*(1,2,3,4))
It is the most formal way to call the function.no problem here.
call method 2:
myadd(1,2,3,4)
It also can get the result,why?
call method 3:
myadd((1,2,3,4))
error TypeError: unsupported operand type(s) for +: 'int' and 'tuple'.
In my point of view,call 2 and call 3 can not be accepted by python,there are no * operator in the position of arguments?can you tell me the pricinple of the operation on python function ?
You're mixing up variable-argument parameters and argument unpacking. This is a common mistake for beginners, because they both use the same * for syntax, and they're not completely unrelated… but they're not nearly as closely related as you think.
These two calls do the exact same thing:
myadd(*(1,2,3,4))
myadd(1, 2, 3, 4)
What the * means here is "take the following iterable, and unpack it into a bunch of separate arguments.
It doesn't matter whether the function you're calling was defined as f(*args), f(a, b, c, d), or f(a, b, *args), you're passing it 4 arguments.
This means method 1 is not "the most formal way to call the function"; in fact, it's just an obfuscated version of method 2.
This, on the other hand, does not do the same thing:
myadd((1, 2, 3, 4))
That passes a single argument, which happens to be a tuple.
So, your function is defined like this:
def myadd(*tuple):
This means whatever it arguments it's passed, no matter how they're passed (except for keyword arguments, but let's ignore that for the moment), they're going to be tossed into a list named tuple. So, let's look at your three cases.
In the first case, you're passing 4 arguments, all of which are integers. So, tuple gets a list of 4 integers. When you iterate over that list, each member is an integer, so you can add them up with no problem.
In the second case—which, again, is exactly the same—you're passing 4 integers, so tuple gets a list of 4 integers.
In the third case, you're passing 1 argument, which is a tuple, so tuple gets a list of 1 tuple. When you iterate over that list, each member is a tuple, and you can't add that to a number.
For more details, see Arguments and parameters, which has links to all the useful places to look in the docs, and hopefully a readable overview.
You are passing the whole tuple as one argument, and tuples cannot be added to numbers. If you want to pass all the tuple elements as individual arguments, use the * operator:
myadd(*x)
def myadd(x):
sum=0
for element in x:
sum=sum+element
return(sum)
x=(1,2,3)
print myadd(x)
output
6

Multi-parameter function - Correct usage of *args / **kwargs

I have a function like this one:
def hexify_string(aString):
#code for string2hexa conversion
...and I want to have a function who accepts one or more (quantity undefined) parameters, and to return the hexadecimal representation of the sum of the parameters.
def hexify(a,b...n):
#map hexify_string to all the parameters, and return the sum of them
Is there a way of doing this using *args / **kwars?
*args takes all non-keyword parameters and turns them into a list. You can map hexify_string to each element of the list, and then return the sum of all the elements in the list.
def hexify(*args);
return sum(map(hexify_string, args)
This is assuming your hexify_string function returns a hex value, not a string.
Is this what you want? Collect any number of arguments, which should all be strings. Pass each string to hexify_string() which returns an integer. Sum the integers and return the sum.
def hexify(*lst):
n = sum(hexify_string(s) for s in lst)
return n
When you want to collect the arguments, you want to treat them all the same, and you don't know how many there will be, that is exactly what the *args syntax was intended for. This is a perfect use case for it.
**kwargs collects name=value arguments, and you don't want any of those here.

Categories