def snd(pair):
x,y = pair
return y
list_of_tuples = [(1,2), (3,4), (5,0)]
print(min(list_of_tuples, key=snd(list_of_tuples)))
the above function takes pair as an argument but when tries calling it, it is called without any arguments.
Why so?
Many functions in Python have an optional key argument that takes a callable (a function) that is applied on each of the elements (here, each tuple pair).
You don't need to supply the argument yourself, min does that for you. From the docs on min:
There are two optional keyword-only arguments. The key argument specifies a one-argument ordering function like that used for list.sort().
Take a look here for additional information on key functions.
Your code was almost correct, just indent after snd defintion and only use the function name for key argument as min function will automatically pass the list as an argument to the function.
def snd(pair):
x,y = pair
return y
list_of_tuples = [(1,2), (3,4), (5,0)]
print(min(list_of_tuples, key=snd))
Related
Im VERY new to programming. Its my second day. In functions like file.read(), empty parenthesis are part of the syntax. Are they always supposed to be empty, or is there an option to fill them? My script works fine, It's just a question I've always had.
When you define a function, you specify the function's arguments. A function can have zero or more arguments. When a function has zero arguments, or all of its arguments have default values, then you can call the function without passing any arguments.
It depends whether you what them empty (no arguments ) or not (with arguments)
Here is an example of a function
#Let's create a function that can add two values :)
def add(x, y): # the function have positional arguments 'x' 'y'
z = x + y
return z # the function output the value of z
addition = add(5, 33) # call the add() function with arrguments x=5, y=33
print(addition)
#this time variables as arguments
a = 5
b = 33
additon = add(a ,b)
print(addition)
As you can see above that function takes input as arguments and returns the output
a tuple in python (in a code block) is defined by the commas; the parentheses are not mandatory (in the cases below). so these three are all equivalent:
a, b = 1, 2
a, b = (1, 2)
(a, b) = 1, 2
if i define a function
def f(a, b):
print(a, b)
calling it this way will work:
f(2, 3)
this will not:
f((2, 3))
# TypeError: f() missing 1 required positional argument: 'b'
how does python treat tuples differently when they are function arguments? here the parentheses are necessary (i understand why this is the case and i am happy python works this way!).
my question is: how does python treat tuples differently when they are function arguments.
For convenience, Python constructs a temporary tuple as needed for an assignment statement. Thus, all three of your assignment statements are exactly the same once they reach data movement.
A function call is not an assignment statement; it's a reference mapping. Therefore, the semantics are different.
If you want Python to unpack your tuple into two separate arguments, use the * operator:
f(*(2, 3))
A tuple behaves like an immutable list. The fact that you notate them with parentheses is perhaps confusing, but it's more or less a coincidence - a result of the fact that parentheses are used for grouping things together and reducing ambiguity otherwise.
When you call a function, you're not providing a tuple. You're providing arguments. A tuple can be an argument, but only one - it's just a variable of type tuple.
What you can do is expand a tuple (or a list) into a a series of arguments with this notation:
tup = (2, 3)
f(*tup)
# expand the tuple (2,3) into a series of arguments 2, 3
You can do that with dictionaries as well, except with ** instead of *:
my_dict = {"arg1": 1, "arg2": 2}
f(arg1=my_dict["arg1"], arg2=my_dict["arg2"])
f(**my_dict) # these are equivalent
On the other hand, functions can take arbitrary numbers of arguments (similar to how other languages do for printf() calls). For example:
def g(*args):
print("I got this many arguments:", len(args))
Here, if you do type(args), you get tuple, and if you do type(*args), you get an error. This is because, in function headers, the * does the exact opposite: it packs the arguments that were given to the function into a single tuple, so that you can work with them. Consider the following:
g(2, 3) # 2 arguments, both integers
g((2, 3)) # 1 argument, a tuple
g(*(2, 3)) # 2 arguments, both integers
In short,
functions are built in such a way that they take an arbitrary number of arguments
The * and ** operators are able to unpack tuples/lists/dicts into arguments on one end, and pack them on the other end
individual tuples/lists/dicts are otherwise just individual variables.
The thing is that parens are used for several different things in Python -- for calling functions, for making tuples (it's not just the commas that matter, see the empty tuple ()), for changing evaluation priority in expressions.
In cases where interpreting them is ambiguous (e.g. your example f(2, 3) could be either a function call with two arguments, or a function call with one argument that is a tuple) the language has to make a choice.
If the Python parser was implemented so that it parsed this as a tuple, it would be impossible to have functions with more than one argument. If the Python parser was implemented so that it parsed this as two arguments, it's impossible to pass a literal tuple without the parens.
Clearly the first is a much bigger limitation, so the second was chosen.
Another example is with tuples with one element -- is (1+2) an expression yielding the number 3, or a tuple with one element, 3? Here if it was the second, then it would be impossible to use parens for expressing priority in an expression ((3+4)*5 vs 3+(4*5)). So it was decided to require the comma after the first element for 1-element tuples ((3,)).
I pass a list to a method which accepts multiple params (*values). If I pass multiple values separated by "," its all fine, but if I pass a list *values turns into a tuple and doesnt iterate the values and the only element is the list I would like to iterate. Can somebody explains that? Is there some way to work around that, that both ways work?
My method:
def accept(*values):
for value in values:
#do something with value
Works:
foo.accept(value1, value2, value3)
Doesnt work:
values = [value1, value2, value3]
foo.accept(values)
Thank you for any help
You need to unpack the list in the function call with the * operator:
foo.accept(*values)
How the multiple parameters feature in python works is that you can pass in a variable number of arguments to the function, and it will automatically turn them into a list (so you don't have to manually) and then execute the code in the function. So when you do foo.accept(value1, value2, value3), it creates a list [value1, value2, value3] and labels this list as the argument to your function, and then you code iterates through the list. However, when you do
values = [value1, value2, value3]
foo.accept(values)
the variable arguments (varargs) feature will wrap your values list into another list so now the argument to your function is [[value1, value2, value3]] and when your for loop does the iteration, it only goes through the one element in this bigger list (the one element being your values list).
Hope this helps!
This syntax is explicitly for taking an arbitrary number of arguments a input
def accept1(*values):
#do something with values
For any given number of arguments it's exactly the same as writing, e.g.:
def accept2(arg1, arg2, arg3):
values = (arg1, arg2, arg3)
#do something with values
To call a function with 3 arguments you can write for both as usual:
accept1(1,2,3)
accept2(1,2,3)
But if you have the arguements in a list or tuple you could do
args = (1,2,3)
accept1(args[0], args[1], args[3])
accept2(args[0], args[1], args[3])
This of course is inflexible and a lot to write so python has a shortcut that again works for an arbitrary number of arguments:
accept1(*args)
accept2(*args)
So in both cases that * denotes a generic way to handle multiple parameters by either
packing individual arguments of a function into a list (in def), or
unpacking a list into individual arguments of a function.
From the Python 2.7.10 documents (slight edit):
4.7.3. Arbitrary Argument Lists
Finally, the least frequently used option is to specify that a function can be called with an arbitrary number of arguments, i.e. using *args. These arguments will be wrapped up in a tuple.
Another point that might help in our understanding is that in Python the parenthesis are redundant in the following line of code:
a = (1, 2, 3)
It would have been enough to write:
a = 1, 2, 3
So onto answering the question. When you have foo.accept(1, 2, 3) the variable arguments get wrapped into a tuple. To verify this step through the code using your favorite debugger into accept() and find that there the arguments have indeed been wrapped in a tuple:
values = (1, 2, 3)
This is what is expected by definition, and the iteration goes over the three elements of the tuple 1, 2, and 3.
On the other hand when you call foo.accept([1, 2, 3]), again, by definition the argument gets wrapped in a tuple. To verify this step through the code into accept() and find:
values = ([1, 2, 3])
This might be unexpected, but it is the way *args are passed. Now the iteration goes over the one element in the tuple [1, 2, 3].
A workaround is usually specific with what is trying to be achieved. But perhaps something as simple as:
foo.accept(1, 2, 3)
or:
foo.accept(*[1, 2, 3])
I created a function that takes an arbitrary number of variables via the *args feature. Now another function needs to call this original function using a list of varying length, but I can't seem to find a solution. As a simplified example:
def print_all(*args):
for x in args:
print(x)
print_all([1,2,3,4,5])
Running this, the console displays:
[1,2,3,4,5]
But I would like it to display:
1
2
3
4
5
Is there a way to turn an iterable like this into proper input for a function that accepts *args like above?
The following will do the trick:
print_all(*[1,2,3,4,5])
With the star operator every item in the list is like been passed as a separate argument to the function.
Remove the * which exists in the parameter part of the function definition. Here * is unnecessary.
def print_all(args):
for x in args:
print(x)
def f1(n): #accepts one argument
pass
def f2(): #accepts no arguments
pass
FUNCTION_LIST = [(f1,(2)), #each list entry is a tuple containing a function object and a tuple of arguments
(f1,(6)),
(f2,())]
for f, arg in FUNCTION_LIST:
f(arg)
The third time round in the loop, it attempts to pass an empty tuple of arguments to a function that accepts no arguments. It gives the error TypeError: f2() takes no arguments (1 given). The first two function calls work correctly - the content of the tuple gets passed, not the tuple itself.
Getting rid of the empty tuple of arguments in the offending list entry doesn't solve the problem:
FUNCTION_LIST[2] = (f2,)
for f,arg in FUNCTION_LIST:
f(arg)
results in ValueError: need more than 1 value to unpack.
I've also tried iterating over the index rather then the list elements.
for n in range(len(FUNCTION_LIST)):
FUNCTION_LIST[n][0](FUNCTION_LIST[n][1])
This gives the same TypeError in the first case, and IndexError: tuple index out of range when the third entry of the list is (f2,).
Finally, asterisk notation doesn't work either. This time it errors on the call to f1:
for f,args in FUNCTION_LIST:
f(*args)
gives TypeError: f1() argument after * must be a sequence, not int.
I've run out of things to try. I still think the first one ought to work. Can anyone point me in the right direction?
Your comment in this code snippet shows a misconception relevant in this context:
FUNCTION_LIST = [(f1,(2)), #each list entry is a tuple containing a function object and a tuple of arguments
(f1,(6)),
(f2,())]
The expressions (2) and (6) are not tuples – they are integers. You should use (2,) and (6,) to denote the single-element tuples you want. After fixing this, your loop code should look thus:
for f, args in FUNCTION_LIST:
f(*args)
See Unpacking Argument Lists in the Python tutorial for an explanation of the *args syntax.
The problem is that such notation:
(6)
evaluates to integer value and you need tuple, so write this way:
(6, )
and your asterisk notation will succeed.
Try passing *() instead of (). The * symbol tells python to unpack the iterable that follows it, so it unpacks the empty tuple and passes nothing to the function, since the tuple was empty.
For the record, a nice alternative I have since discovered is the use of functools.partial. The following code does what I was trying to do:
from functools import partial
def f1(n): #accepts one argument
pass
def f2(): #accepts no arguments
pass
FUNCTION_LIST = [partial(f1,2), #each list entry is a callable with the argument pre-ordained
partial(f1,6),
partial(f2)] #the call to partial is not really necessary for the entry with no arguments.
for f in FUNCTION_LIST: f()