Why can't I use a starred expression?

2019-03-09 03:04发布

问题:

My code

$ python
Python 3.5.2 |Continuum Analytics, Inc.| (default, Jul  2 2016, 17:53:06) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 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.

回答1:

It's because this:

(a)

Is just a value surrounded by parenthesis. It's not a new tuple object. So your expression:

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

will get translated to:

>>> '%d %d' % * a

which is obviously wrong in terms of python syntax.

In order to create a new Tuple, with one expression as an initializer, you need to add a ',' after it:

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

Note: unless a is a generator, in this particular situation you could just type:

>>> '%d %d' % a

Also, if I may suggest something: you could start using new-style formating expressions. They are great!

>>> "{} {}".format(*a)

You can read more about them in those two paragraphs of python documentation, also there is this great website. The line above uses argument unpacking mechanism described below.

Starred Expressions

There are many more uses to starred expression than just creating a new list/tuple/dictionary. Most of them are described in this PEP, and this one

All of them come down to two kinds:

RValue unpacking:

>>> a, *b, c = range(5)
# a = 0
# b = [1, 2, 3]
# c = 4
>>> 10, *range(2)
(10, 0, 1)

Iterable / dictionary object initialization (notice that you can unpack dictionaries inside lists too!):

>>> [1, 2, *[3, 4], *[5], *(6, 7)]
[1, 2, 3, 4, 5, 6, 7]
>>> (1, *[2, 3], *{"a": 1})
(1, 2, 3, 'a')
>>> {"a": 1, **{"b": 2, "c": 3}, **{"c": "new 3", "d": 4}}
{'a': 1, 'b': 2, 'c': 'new 3', 'd': 4}

Of course, the most often seen use is arguments unpacking:

positional_arguments = [12, "a string", (1, 2, 3), other_object]
keyword_arguments = {"hostname": "localhost", "port": 8080}
send(*positional_arguments, **keyword_arguments)

which would translate to this:

send(12, "a string", (1, 2, 3), other_object, hostname="localhost", port=8080)

This topic was already covered in great extent in another Stack Overflow question.



回答2:

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


回答3:

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