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

Steven D'Aprano steve at pearwood.info
Thu Oct 13 21:04:07 EDT 2016


On Thu, Oct 13, 2016 at 08:15:36PM +0200, Martti Kühne wrote:

> Can I fix my name, though?

I don't understand what you mean. Your email address says your name is 
Martti Kühne. Is that incorrect?



[...]
> I meant that statement in context of the examples which were brought up:
> the occurrence of a list comprehension inside an array have the
> following effect:
> 
> 1) [ ..., [expr for t in iterable] ]
> 
> is equivalent to:
> 
> def expr_long(iterable, result):
>     result.append(iterable)
>     return result
> 
> expr_long(iterable, [ ..., ])

The good thing about this example is that it is actual runnable code 
that we can run to see if they are equivalent. They are not equivalent.

py> def expr_long(iterable, result):
...     result.append(iterable)
...     return result
...
py> iterable = (100, 200, 300)
py> a = [..., [2*x for x in iterable]]
py> b = expr_long(iterable, [...])
py> a == b
False
py> print(a, b)
[Ellipsis, [200, 400, 600]] [Ellipsis, (100, 200, 300)]

For this to work, you have to evaluate the list comprehension first, 
then pass the resulting list to be appended to the result.

I don't think this is very insightful. All you have demonstrated is that 
a list display [a, b, c, ...] is equivalent to:

result = []
for x in [a, b, c, ...]:
    result.append(x)


except that you have written it in a slightly functional form.


> so, if you make the case for pep448, you might arrive at the following:
> 
> 2) [ ..., *[expr for expr in iterable] ]

That syntax already works (in Python 3.5):

py> [1, 2, 3, *[x+1 for x in (100, 200, 300)], 4, 5]
[1, 2, 3, 101, 201, 301, 4, 5]


> which would be, if I'm typing it correctly, equivalent to, what
> resembles an external collection:
> 
> def expr_star(list_comp, result):
>     result.extend(list(list_comp))
>     return result
> 
> expr_star(iterable, [ ..., ])
> 
> Having this in mind, the step to making:
> 
> [ ..., [*expr for expr in iterable], ]
> 
> from:
> 
> def expr_insidestar(iterable, result):
>     for expr in iterable:
>         result.extend(expr)
>     return result
> 
> does not appear particularly far-fetched, at least not to me and a few
> people on this list.

But you don't have [..., list_comp, ] you just have the list comp.

You are saying:

(1) List displays [a, b, c, d, ...] are like this; 
(2) we can sensibly extend that to the case [a, b, *c, d, ...]

I agree with (1) and (2). But then you have a leap:

(3) therefore [*t for t in iterable] should mean this.

There's a huge leap between the two.

To even begin to make sense of this, you have to unroll the list 
comprehension into a list display. But that's not very helpful:

    [expr for t in iterable]

Would you rather see that explained as:

    [expr, expr, expr, expr, ...] 

or as this?

    result = []
    for t in iterable:
        result.append(expr)


The second form, the standard, documented explanation for 
comprehensions, also applies easily to more complex examples:

[expr for t in iter1 for u in iter2 for v in iter3 if condition]

result = []
for t in iter1:
    for u in iter2:
        for v in iter3:
            if condition:
                result.append(expr)



-- 
Steve


More information about the Python-ideas mailing list