What does "*list" mean?

Michael Sparks zathras at thwackety.com
Thu Jul 31 18:13:05 EDT 2003


On 31 Jul 2003, Greg Smethells wrote:

> What exactly does "*values" mean in the following code?

One way of viewing it is that it flattens the list - expands the list as
separate arguments to be passed to the function. For those coming from a
perl background it makes the list act in the same way as a perl list
passed to a function not as a reference.

For sake of argument I'll  use the following list, and 3 functions:

values=[1,2,3] # For sake of argument
def bar(args):
   print args

def bar(args): # Simple case
   print args

def foo(*args): # Takes the arguments and turns it into tuple
   print args

def foobar(arg, *args): # Take first argument, and treat all others as
                        # part of a tuple called args
   print arg, ":", args

Let's do the simple case first:
>>> bar(values)
[1, 2, 3]

As you expect this succeeds - we passed one value to the function.

>>> bar(*values)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: bar() takes exactly 1 argument (3 given)

The reason this failed is because
>>> bar(*values)
Is essentially equivalent to this:
>>> bar(1,2,3)

Due to the "flattening" of the list.

The next case is where un-flattening happens (there's probably a better
phrase here :) :

>>> foo(values)
([1, 2, 3],)

What's happened here? The call that was made is essentially this:
>>> foo([1,2,3])

This hit the following signature:
def foo(*args):

This says to take the arguments, and stuff them into a tuple. We've only
passed one argument - specifically [1,2,3]. So a tuple is created using
this, which gives us our result : ([1,2,3], ) is displayed.

If we call foo with a normal argument list we get:
>>> foo(1,2,3)
(1, 2, 3)

Why? Because *args in this context _effectively_ says "take the
(remaining) arguments, and create a single value named 'args' that is a
tuple containing those values". ie take the arguments 1,2,3, and create a
tuple - which is (1,2,3) which then gets displayed.

If we combine these things we quickly find some fun stuff. What happens if
we flatten a list that gets sent to a function that turns it's argument
list into a tuple?

>>> foo(*values)
(1, 2, 3)

We get this because foo(*values) is directly equivalent to foo(1,2,3) -
the list *values gets expanded/flattened.

Taking the final example function - what happens if you mix and match?

>>> foobar(values)
[1, 2, 3] : ()

Well, our signature here was:
>>> def foobar(arg, *args):

This says - take the first argument in the parameter list and stuff it in
"arg", if you get any other values, create a tuple with them, and stuff
them in args. In this case the first argument was the list [1,2,3], and
there were no further arguments, so args contains an empty tuple ()

If however we expand/flatten out list:
>>> foobar(*values)
1 : (2, 3)

Our result is pretty clear: foobar(*values) has been treated as
foobar(1,2,3), the first argument -1- stuffed into arg, and the other two
stuffed into a tuple in args (1,2).

As a result this:

> >>> from struct import *
> >>> format = "dl"
> >>> values = [3.14, 42]
> >>> foo = pack(format, *values)

is directly equivalent to:

>>> from struct import *
>>> foo = pack("dl", 3.14, 42)
>>> foo
'\x1f\x85\xebQ\xb8\x1e\t@*\x00\x00\x00'


> '\x1f\x85\xebQ\xb8\x1e\t@*\x00\x00\x00'
> More importantly still, how would you write the same code in C? If you
> wanted to instead call struct_pack(PyObject *tuple), what would the
> tuple look like in C?

Caveat: I haven't written any C extensions, not looked into the API and so
on, but I will point out this minor thing: how does this python function
differ from a normal one?

>>> def hello(world, everyone):
...    print "hello", world, everyone
...
>>> values=["world","today"]
>>> hello(*values)
hello world today

Answer: it doesn't - it's just how the argument list has been built up. If
you were trying for the same effect in C, in principle, you'd either need:

   * To do a varags call - ala printf (in which case the signature of what
     you're calling would need to match) (This would match the "foobar"
     example above)
   * To upack the tuple into temporary values and then do the call
     manually - as you could in python:

>>> values=["world","today"]
>>> arg1=values[0]
>>> arg2=values[1]
>>> hello(arg1,arg2)
hello world today

There's probably a formal description of all this stuff somewhere :-)


Michael.






More information about the Python-list mailing list