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()
Related
So, I'm instantiating a class several times using a tuple -- every tuple item is a class instance in the format
ClassName(argument 1, ... argument n)
Now, I'd like one of the arguments to be the index of the item in the tuple, but I don't know how to express that in Python. I tried using
my_tuple.index(ClassName)
but that didn't work. Any other ideas?
You can use a list comprehension.
tuple([ClassName(i, argument 2, ..., argument n) for i in range(however_many_you_want)])
This will create a list which will then be converted into a tuple.
Here, the first argument is being used as the index in the tuple.
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,)).
Suppose i have a list:
a=['hello.com','ok.com']
i have two function:
def f0(id):
try:
model.objects.get(links=id)
except:
model(modelfield=id).save()
def f1(request):
for i in a:
t1=thread.Thread(target=f0,args=(i))
t1.start()
While i try to run this on my server , its giving me error .
TypeError: f0() takes 1 positional argument but 9 were given
Kindly tell me whats the problem .
You are passing in a single string as the args value:
args=(i)
That's not a tuple, that's a grouped expression containing just 'hello.com', an iterable with 9 separate elements (single-character strings).
Add a comma; tuples are formed by the comma, not the parentheses (although you need parentheses to disambiguate the tuple from other arguments in a call):
args=(i,)
or if you find that confusing, use a list:
args=[i]
In 'args=(i)', (i) is not a tuple.
Convert (i) into a tuple by appending , so the right statement will be:
t1=thread.Thread(target=f0,args=(i,))
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))
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])