[Python-ideas] Fwd: unpacking generalisations for list comprehension

Steven D'Aprano steve at pearwood.info
Wed Oct 12 19:29:43 EDT 2016


On Wed, Oct 12, 2016 at 06:32:12PM +0200, Sven R. Kunze wrote:
> On 12.10.2016 17:41, Nick Coghlan wrote:
> >This particular proposal fails on the first question (as too many
> >people would expect it to mean the same thing as either "[*expr, for
> >expr in iterable]" or "[*(expr for expr in iterable)]")
> 
> So, my reasoning would tell me: where have I seen * so far? *args and 
> **kwargs! 

And multiplication. And sequence unpacking.

> [...] is just the list constructor. 

Also indexing: dict[key] or sequence[item or slice].

The list constructor would be either list(...) or possibly 
list.__new__.

[...] is either a list display:

    [1, 2, 3, 4]

or a list comprehension. They are not the same thing, and they don't 
work the same way. The only similarity is that they use [ ] as 
delimiters, just like dict and sequence indexing. That doesn't mean that 
you can write:

    mydict[x for x in seq if condition]

Not everything with [ ] is the same.


> So, putting those two pieces together is quite simple.

I don't see that it is simple at all. I don't see any connection between 
function *args and list comprehension loop variables.



> Furthermore, your two "interpretations" would yield the very same result 
> as [expr for expr in iterable] which doesn't match with my experience 
> with Python so far; especially when it comes to special characters. They 
> must mean something. So, a simple "no-op" would not match my expectations.

Just because something would otherwise be a no-op doesn't mean that it 
therefore has to have some magical meaning. Python has a few no-ops 
which are allowed, or required, by syntax but don't do anything.

   pass
   (x) # same as just x
   +1  # no difference between literals +1 and 1
   -0
   func((expr for x in iterable))  # redundant parens for generator expr

There may be more.



> >but it fails on the other two grounds as well.
> 
> Here I disagree with you. We use *args all the time, so we know what * 
> does. I don't understand why this should not work in between brackets [...].

By this logic, *t should work... everywhere?

    while *args:
        try:
            raise *args
        except *args:
            del *args

That's not how Python works. Just because syntax is common, doesn't mean 
it has to work everywhere. We cannot write:


    for x in import math:
        ...

even though importing is common.

*t doesn't work as the expression inside a list comprehension because 
that's not how list comps work. To make it work requires making this a 
special case and mapping 

    [expr for t in iterable]

to a list append, while 

    [*expr for t in iterable]

gets mapped to a list extend.

Its okay to want that as a special feature, but understand what you are 
asking for: you're not asking for some restriction to be lifted, which 
will then automatically give you the functionality you expect. You're 
asking for new functionality to be added.

Sequence unpacking inside list comprehensions as a way of flattening a 
sequence is completely new functionality which does not logically follow 
from the current semantics of comprehensions.



> >In most uses of *-unpacking it's adding entries to a comma-delimited
> >sequence, or consuming entries in a comma delimited sequence (the
> >commas are optional in some cases, but they're still part of the
> >relevant contexts). The expansions removed the special casing of
> >functions, and made these capabilities generally available to all
> >sequence definition operations.
> 
> I don't know what you mean by comma-delimited sequence. There are no 
> commas. It's just a list of entries. * adds entries to this list. (At 
> least from my point of view.)

Not all points of view are equally valid.



-- 
Steve


More information about the Python-ideas mailing list