tuples as function arguments

36,065

Solution 1

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))

Solution 2

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.

Solution 3

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,)).

Share:
36,065
hiro protagonist
Author by

hiro protagonist

theoretical physicist turned computer scientist (security, crypto). programming languages: python, rust, (c, c++, java).

Updated on June 08, 2020

Comments

  • hiro protagonist
    hiro protagonist almost 4 years

    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.

  • RemcoGerlich
    RemcoGerlich almost 5 years
    I feel this doesn' t really answer his question, he's asking why the 2, 3 in func(2, 3) isn't a tuple. It should be if the parens were that optional.
  • Green Cloak Guy
    Green Cloak Guy almost 5 years
    Because that's how the language is defined. A tuple is a datatype, just like any other type of variable. Functions take a sequence of arguments (of various types) - not just one argument that happens to hold the rest of the arguments. While it's true a number of functions (e.g. threading stuff) use tuples to specify what arguments to provide to functions, that's out of necessity (providing a set of args as a variable).
  • hiro protagonist
    hiro protagonist almost 5 years
    A function call is not an assignment statement; it's a reference mapping. thanks for that! (i should really have learnt that by now!)
  • Dunes
    Dunes almost 5 years
    "Python constructs a temporary tuple" -- conceptually this is what Python does, but in reality it just pushes objects onto a stack and pops them off again. No tuple is ever created. Try seeing what dis.dis outputs for a function that executes a, b = b, a.