52

I tried the following using the REPL in Python 3.5.2:

>>> a = (1, 2)
>>> '%d %d %d' % (0, *a)
'0 1 2'
>>> '%d %d %d' % (*a, 3)
'1 2 3'
>>> '%d %d' % (*a)
  File "<stdin>", line 1
SyntaxError: can't use starred expression here
>>> 

My question, why?

In a more serious tone: I'd like an answer, or a reference, that details all the ins and outs of using a starred expression, as it happens that I am sometimes surprised from its behaviours...

Addendum

To reflect some of the enlightening comments that immediately followed my question I add the following code

>>> '%d %d' % (, *a)
  File "<stdin>", line 1
    '%d %d' % (, *a)
               ^
SyntaxError: invalid syntax
>>> '%d %d' % (*a,)
'1 2'
>>> 

(I had tried the (, a) part before posting the original question but I've omitted it 'cause the error was not related to the starring.)

There is a syntax, in python ≥ 3.5, that "just works" but nevertheless I would like some understanding.

metatoaster
  • 17,419
  • 5
  • 55
  • 66
gboffi
  • 22,939
  • 8
  • 54
  • 85
  • Take a look at the "Unpacking Iterables" documentation on SO https://stackoverflow.com/documentation/python/809/compatibility-between-python-3-and-python-2/2845/unpacking-iterables#t=201611181159516910043 – pylang Nov 18 '16 at 12:01
  • Interestingly, the `format()` function does not have this issue, i.e. `'{:d} {:d}'.format(*a)` --> `'1, 2'`. The issue appears related to the `%` string formatter. – pylang Nov 18 '16 at 12:08
  • 2
    @pylang That's because you are using argument unpacking there, not trying to create a tuple. See my edited answer below. – Błażej Michalik Nov 18 '16 at 12:34
  • **Moderator note**: This is temporarily locked because these repeated rollbacks are not OK. If you find yourself in a rollback war, **flag much earlier**. The edits made by the community here were an improvement, and I'd like to keep those here. Questions are here to help future users, and the improvements to the title and readability make the process of assessing what is being asked easier. – Martijn Pieters Feb 14 '23 at 17:32

3 Answers3

63

The error occurs because (a) is just a value surrounded by parenthesis. It's not a new tuple object.

Thus, '%d %d' % (*a) is equivalent to '%d %d' % * a, which is obviously wrong in terms of python syntax.

To create a new tuple, with one expression as an initializer, use a comma after that expression:

>>> '%d %d' % (*a,)
'1 2'

Of course, since a is already a tuple, we can use it directly:

>>> '%d %d' % a
'1 2'
Błażej Michalik
  • 4,474
  • 40
  • 55
  • The original version of this answer included a lot of material that is not appropriate in a proper Stack Overflow Q&A, responding to extra material in the question. In particular, it explained about the unpacking syntax more generally (in a way that is better covered by a different canonical, which I have now linked in the question), and discussed (unprompted) other ways to format the string. – Karl Knechtel Feb 09 '23 at 13:04
  • Those other string formatting techniques are also properly covered by existing canonicals. For the sake of preserving the information here: Please see [How do I put a variable’s value inside a string (interpolate it into the string)?](/q/2960772) for a full rundown of string formatting/interpolation techniques, and [String formatting: % vs. .format vs. f-string literal](/q/5082452) for a comparison of `.format` and f-string techniques to `%`-style formatting. – Karl Knechtel Feb 09 '23 at 13:07
4

My question, why?

Because your python syntax doesn't allow that. It's defined that way, so there's no real "why".

also, it's unnecessary.

"%d %d" % a

would work.

So, you'd need to convert your expansion to a tuple – and the right way of doing that would be, as pointed out by Lafexlos, be

"%d %d" % (*a,)
Marcus Müller
  • 34,677
  • 4
  • 53
  • 94
  • 1
    Re `"%d %d" % a`, my _real_ use case involves a generator expression…. In the question I wanted to keep things simple and, moreover, I really want to know the details of unpacking – gboffi Nov 18 '16 at 12:06
2

It's because:

>>> '%d %d' % (*a)

Can be just:

>>> '%d %d' %a

Of course then able to do:

>>> '%d %d' % (*a,)

But then:

>>> (*a,)==a
True
>>> 

Or you can do:

>>> '%d %d' % [*a]

But then:

>>> [*a]
[1, 2]
>>> a
(1, 2)
>>> 

So:

>>> tuple([*a])==a
True
U13-Forward
  • 69,221
  • 14
  • 89
  • 114