552

Suppose I have a function like:

def myfun(a, b, c):
    return (a * 2, b + c, c + b)

Given a tuple some_tuple = (1, "foo", "bar"), how would I use some_tuple to call myfun? This should output the result (2, "foobar", "barfoo").

I know could define myfun so that it accepts the tuple directly, but I want to call the existing myfun.


See also: What do ** (double star/asterisk) and * (star/asterisk) mean in a function call?.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
AkiRoss
  • 11,745
  • 6
  • 59
  • 86

5 Answers5

963

myfun(*some_tuple) does exactly what you request. The * operator simply unpacks the tuple (or any iterable) and passes them as the positional arguments to the function. Read more about unpacking arguments.

Nicolas Gervais
  • 33,817
  • 13
  • 115
  • 143
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 15
    The * operator simply unpacks the tuple and passes them as the positional arguments to the function. See more here: https://docs.python.org/3/tutorial/controlflow.html#tut-unpacking-arguments – john_mc Jun 30 '17 at 21:09
  • 4
    Note that the same syntax can be used for lists as well as tuples. – brendon-ai Aug 17 '17 at 13:22
  • I've found that you can do the same with lists, (in fact, any iterable, including strings), not sure how their mutability affects things. That would be interesting to look into. – wcyn Nov 26 '17 at 11:47
76

Note that you can also expand part of argument list:

myfun(1, *("foo", "bar"))
Valentas
  • 2,014
  • 20
  • 24
  • 17
    It appears you can only do this if the expanded tuple is after the normally-provided arguments - the interpreter doesn't like it when I do this: `some_func(*tuple_of_stuff, another_argument)` – Tom Galvin Apr 19 '15 at 20:46
  • 7
    @Quackmatic Having the expanded tuple in any location seems to work fine in Python 3.5.1 – River Jun 13 '16 at 13:00
  • 1
    @Quackmatic seconding @River, this works fine in Python 3.5.4: `def func(a,b,c,d): print(a,b,c,d)` with `args = ('fee', 'fi', 'fo'); func(*args, 'fum')` – R. Navega Sep 06 '18 at 13:25
16

Take a look at the Python tutorial section 4.7.3 and 4.7.4. It talks about passing tuples as arguments.

I would also consider using named parameters (and passing a dictionary) instead of using a tuple and passing a sequence. I find the use of positional arguments to be a bad practice when the positions are not intuitive or there are multiple parameters.

Kellen Donohue
  • 777
  • 8
  • 17
Uri
  • 88,451
  • 51
  • 221
  • 321
10

This is the functional programming method. It lifts the tuple expansion feature out of syntax sugar:

apply_tuple = lambda f, t: f(*t)

Redefine apply_tuple via curry to save a lot of partial calls in the long run:

from toolz import curry
apply_tuple = curry(apply_tuple)

Example usage:

from operator import add, eq
from toolz import thread_last

thread_last(
    [(1,2), (3,4)],
    (map, apply_tuple(add)),
    list,
    (eq, [3, 7])
)
# Prints 'True'
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Dominykas Mostauskis
  • 7,797
  • 3
  • 48
  • 67
6

Similar to @Dominykas's answer, this is a decorator that converts multiargument-accepting functions into tuple-accepting functions:

apply_tuple = lambda f: lambda args: f(*args)

Example 1:

def add(a, b):
    return a + b

three = apply_tuple(add)((1, 2))

Example 2:

@apply_tuple
def add(a, b):
    return a + b

three = add((1, 2))
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135