What is the difference between using *args and getting a list as input for having a dynamic number of inputs for a function.
Do these two ways have any advantages in comparison with the other?
It's just a difference in what type of arguments the function expects. They are fairly interchangeable; which you choose is mostly a matter of preference (though see the exceptions at the end of the answer).
Given the following definitions:
# Separate arguments
def foo(*args):
pass
# Single sequence of arguments
def bar(list_of_args):
pass
x = [1, 2, 3]
These two calls are equivalent:
foo(1, 2, 3)
foo(*x) # Unpack a list before calling
And these two calls are equivalent:
bar(x)
bar([1, 2, 3]) # Explicitly pack individual arguments
You mentioned **args in your question. The name of the parameter isn't really important; the special behavior is indicated by a parameter name beginning with one or two *s. One * indicates arbitrary positional arguments, as shown above. Two *s indicates arbitrary keyword arguments. Conventionally, the two most common names are *args and **kwargs.
A brief example similar to the above:
# Arbitrary keyword arguments
def foo(**kwargs):
pass
# Single dict d
def bar(d):
pass
d = {'a': 1, 'b': 2}
# Equivalent
foo(*d)
foo(a=1, b=2)
# Equivalent
bar(d)
bar(dict(a=1, b=2))
Exception: if the function actually expects to mutate a value, you can't replace that effectively with a */** parameter. Example:
def foo(**kwargs):
kwargs['foo'] = 3
This adds a mapping of 'foo' to 3 to the dictionary, but it is a temporary dictionary created just for the call to foo. Nothing outside the function has a reference to that dictionary, so the update doesn't have any effect after foo returns.
Similar logic applies to *args, but since args is a tuple, and tuples are immutable, there's little you could do with the argument anyway. That said, if the function really expects a list, and not just an arbitrary sequence of arguments, you have to define your function to handle that.
def foo(some_list): # can't use *args here
some_list.append(3)
You can't effectively define that as
def foo(*args):
args.append(3)
because foo receives a temporary tuple, not a list object that
Related
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")
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()
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
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).
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument
I'm kind of confused about how optional parameters work in Python functions/methods.
I have the following code block:
>>> def F(a, b=[]):
... b.append(a)
... return b
...
>>> F(0)
[0]
>>> F(1)
[0, 1]
>>>
Why F(1) returns [0, 1] and not [1]?
I mean, what is happening inside?
Good doc from PyCon a couple years back - Default parameter values explained. But basically, since lists are mutable objects, and keyword arguments are evaluated at function definition time, every time you call the function, you get the same default value.
The right way to do this would be:
def F(a, b=None):
if b is None:
b = []
b.append(a)
return b
Default parameters are, quite intuitively, somewhat like member variables on the function object.
Default parameter values are evaluated when the function definition is executed. This means that the expression is evaluated once, when the function is defined, and that same “pre-computed” value is used for each call. This is especially important to understand when a default parameter is a mutable object, such as a list or a dictionary: if the function modifies the object (e.g. by appending an item to a list), the default value is in effect modified.
http://docs.python.org/reference/compound_stmts.html#function
Lists are a mutable objects; you can change their contents. The correct way to get a default list (or dictionary, or set) is to create it at run time instead, inside the function:
def good_append(new_item, a_list=None):
if a_list is None:
a_list = []
a_list.append(new_item)
return a_list