That's a recipe for much deeper confusion, as it would make "*expr"
and "*expr," semantically identical
>>> *range(3),
(0, 1, 2)
As things stand, the above makes tuple expansion the same as any other
expression: you need a comma to actually make it a tuple. If you allow
a bare "*" to imply the trailing comma, then it immediately becomes
confusing when you actually *do* have a comma present, as the "*" no
longer implies a new tuple, it gets absorbed into the surrounding one.
That's outright backwards incompatible with the status quo once you
take parentheses into account:
>>> (*range(3)),
(0, 1, 2)
Regards,
Nick.
--
Nick Coghlan | ncog...@gmail.com | Brisbane, Australia
Steve, you only need to allow multiple arguments to append(), then it makes perfect sense.
> Steve, you only need to allow multiple arguments to append(), then it makes
> perfect sense.
No, because that would be explicit. Here it's implicit and ambiguous.
Specifically, it requires guessing "operator associativity". That is
something people have different intuitions about.
> > On Tue, Oct 11, 2016 at 02:42:54PM +0200, Martti Kühne wrote:
> > > Hello list
> > >
> > > I love the "new" unpacking generalisations as of pep448. And I found
> > > myself using them rather regularly, both with lists and dict.
> > > Today I somehow expected that [*foo for foo in bar] was equivalent to
> > > itertools.chain(*[foo for foo in bar]), which it turned out to be a
> > > SyntaxError.
Which is what I myself would expect, same as *(1, 2) + 3 is a
SyntaxError. I could see Nick's interpretation that *foo in such a
context would actually mean (*foo,) (i.e., it casts iterables to
tuples). I would certainly want [i, j for i, j in [[1, 2], [3, 4]]]
to evaluate to [(1, 2), (3, 4)] (if it weren't a SyntaxError).
> > To me, that's a very strange thing to expect. Why would you expect that
> > unpacking items in a list comprehension would magically lead to extra
> > items in the resulting list? I don't think that makes any sense.
Well, that's what it does in display syntax for sequences. If you
think of a comprehension as a "macro" that expands to display syntax,
makes some sense. But as you and Nick point out, comprehensions are
real operations, not macros which implicitly construct displays, then
evaluate them to get the actual sequence.
> > Wishful thinking perhaps?
That was unnecessary. I know sometimes I fall into the trap of
thinking there really ought to be concise syntax for a "simple" idea,
and then making one up rather than looking it up.
I've followed this discussion some, and every example given so far completely mystifies me and I have no intuition about what they should mean.
What is the intuition behind [1, *x, 5]? The starred expression is replaced with a comma-separated sequence of its elements.
The trailing comma Nick referred to is there, with the rule that [1,, 5] is the same as [1, 5].
All the examples follow this intuition, IIUC.
Elazar
What is the intuition behind [1, *x, 5]? The starred expression is replaced with a comma-separated sequence of its elements.
Same here.
On 12 October 2016 at 20:38, אלעזר <ela...@gmail.com> wrote:
> What is the intuition behind [1, *x, 5]? The starred expression is replaced
> with a comma-separated sequence of its elements.
>
> The trailing comma Nick referred to is there, with the rule that [1,, 5] is
> the same as [1, 5].
>
> All the examples follow this intuition, IIUC.
But intuition is precisely that - it's not based on rules, but on
people's instinctive understanding. When evaluating whether something
is intuitive, the *only* thing that matters is what people tell you
they do or don't understand by a given construct. And in this case,
people have been expressing differing interpretations, and confusion.
That says "not intuitive" loud and clear to me.
And yes, I find [1, *x, 5] intuitive. And I can't tell you why I find
it OK, but I find {**x for x in d.items()} non-intuitive. But just
because I can't explain it doesn't mean it's not true, or you can
"change my mind" about how I feel.
Paul
On Wed, Oct 12, 2016 at 12:38 PM, אלעזר <ela...@gmail.com> wrote:What is the intuition behind [1, *x, 5]? The starred expression is replaced with a comma-separated sequence of its elements.
I've never actually used the `[1, *x, 5]` form. And therefore, of course, I've never taught it either (I teach Python for a living nowadays). I think that syntax already perhaps goes too far, actually; but I can understand it relatively easily by analogy with:a, *b, c = range(10)
But the way I think about or explain either of those is "gather the extra items from the sequence." That works in both those contexts. In contrast:>>> *b = range(10)SyntaxError: starred assignment target must be in a list or tupleSince nothing was assigned to a non-unpacked variable, nothing is "extra items" in the same sense. So failure feels right to me. I understand that "convert an iterable to a list" is conceptually available for that line, but we already have `list(it)` around, so it would be redundant and slightly confusing.
What seems to be wanted with `[*foo for foo in bar]` is basically just `flatten(bar)`. The latter feels like a better spelling, and the recipes in itertools docs give an implementation already (a one-liner).We do have a possibility of writing this:>>> [(*stuff,) for stuff in [range(-5,-1), range(5)]][(-5, -4, -3, -2), (0, 1, 2, 3, 4)]That's not flattened, as it should not be. But it is very confusing to have `[(*stuff) for stuff in ...]` behave differently than that. It's much more natural—and much more explicit—to write:>>> [item for seq in [range(-5,-1), range(5)] for item in seq][-5, -4, -3, -2, 0, 1, 2, 3, 4]
I have to admit that I have my problems with this "comma-separated
sequence" idea. For me, lists are just collections of items. There are
no commas involved. I also think that thinking about commas here
complicates the matter.
What * does, it basically plugs in the items from the starred expression
into its surroundings:
[*[1,2,3]] = [1,2,3]
Let's plug in two lists into its surrounding list:
[*[1,2,3], *[1,2,3]] = [1,2,3,1,2,3]
So, as the thing goes, it looks like as if * could just work anywhere
inside those brackets:
[*[1,2,3] for _ in range(3)] = [*[1,2,3], *[1,2,3], *[1,2,3]] =
[1,2,3,1,2,3,1,2,3]
I have difficulties to understand the problem of understanding the
syntax. The * and ** variants just flow naturally whereas the "chain"
equivalent is bit "meh".
Cheers,
Sven
> Steve, you only need to allow multiple arguments to append(), then it makes
> perfect sense.
I think you're missing a step. What will multiple arguments given to
append do? There are two obvious possibilities:
- collect all the arguments into a tuple, and append the tuple;
- duplicate the functionality of list.extend
neither of which appeals to me.
On Wed, Oct 12, 2016 at 04:11:55PM +0000, אלעזר wrote:
> Steve, you only need to allow multiple arguments to append(), then it makes
> perfect sense.
I think you're missing a step. What will multiple arguments given to
append do? There are two obvious possibilities:
- collect all the arguments into a tuple, and append the tuple;
- duplicate the functionality of list.extend
neither of which appeals to me.
On Thu, Oct 13, 2016 at 10:37:35AM +0200, Sven R. Kunze wrote:
> About the list constructor: we construct a list by writing [a,b,c] or by
> writing [b for b in bs]. The end result is a list
I construct lists using all sorts of ways:
On 13 October 2016 at 15:32, Sven R. Kunze <srk...@mail.de> wrote:
> Steven, please. You seemed to struggle to understand the notion of the
> [*....] construct and many people (not just me) here tried their best to
> explain their intuition to you.
And yet, the fact that it's hard to explain your intuition to others
(Steven is not the only one who's finding this hard to follow) surely
implies that it's merely that - personal intuition - and not universal
understanding.
The *whole point* here is that not everyone understands the proposed
notation the way the proposers do, and it's *hard to explain* to those
people. Blaming the people who don't understand does not support the
position that this notation should be added to the language. Rather,
it reinforces the idea that the new proposal is hard to teach (and
consequently, it may be a bad idea for Python).
> It may also suggest that there are currently two ways to understand the
> *[...] construct,
This thread is about allowing sequence unpacking as the internal
expression of list comprehensions:
[ *(expr) for x in iterable ]
It isn't about unpacking lists:
*[...]
so I don't see what relevance your comment has.
There may be two or three or ten or 100 ways to (mis)understand list
comprehensions in Python, but only one of them is the correct way. List
comprehensions are (roughly) syntactic sugar for:
result = []
for x in iterable:
result.append(expression)
Any other understanding of them is incorrect.
Now if people wish to make an argument for changing the meaning of
comprehensions so that the suggested internal unpacking makes sense,
then by all means try making that argument! That's absolutely fine.
In the past, I've tried a similar thing: I argued for a variant list
comprehension that halts early:
[expr for x in iterable while condition]
(as found in at least one other language), but had that knocked back
because it doesn't fit the existing list comprehension semantics. I
wasn't able to convince people that the value of this new comprehension
was worth breaking the existing semantics of comprehensions.
Maybe you will be able to do better than me.
But understand that:
[*(expr) for x in iterable]
also fails to fit the existing list comprehension semantics. To make it
work requires changing the meaning of Python list comps. It isn't enough
to just deny the existing meaning and insist that your own personal
meaning is correct.
--
Steve
Sorry for misquoting you. Can I fix my name, though?
Also, this mail was too long in my outbox so the context was lost on it.
I reiterate it, risking that I would annoy some, but to be absolutely clear.
>
>> As it happens, python does have an external consumption operation that
>> happens externally with an iteration implied:
>>
>
> If you replace the t with *t, you get a syntax error:
>
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, [ ..., ])
so, if you make the case for pep448, you might arrive at the following:
2) [ ..., *[expr for expr in iterable] ]
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.
cheers!
mar77i
[*t for t in [(1, 'a'), (2, 'b'), (3, 'c')]]
I remain puzzled.
Given the well-documented and understood transformation:
[fn(x) for x in lst if cond]
translates to
result = []
for x in lst:
if cond:
result.append(fn(x))
please can you explain how to modify that translation rule to
incorporate the suggested syntax?
Personally, I'm not even sure any more that I can *describe* the
suggested syntax. Where in [fn(x) for x in lst if cond] is the *
allowed? fn(*x)? *fn(x)? Only as *x with a bare variable, but no
expression? Only in certain restricted types of construct which aren't
expressions but are some variation on an unpacking construct?
We've had a lot of examples. I think it's probably time for someone to
describe the precise syntax (as BNF, like the syntax in the Python
docs at https://docs.python.org/3.6/reference/expressions.html#displays-for-lists-sets-and-dictionaries
and following sections) and semantics (as an explanation of how to
rewrite any syntactically valid display as a loop). It'll have to be
done in the end, as part of any implementation, so why not now?
I don't (for the reasons raised before). But thank you for your
explanation, it clarifies what you were proposing. And it does so
within the *current* uses of the * symbol, which is good. But:
1. I'm not keen on extending append's meaning to overlap with extend's
like this.
2. Your proposal does not generalise to generator expressions, set
displays (without similarly modifying the set.add() method) or
dictionary displays.
3. *fn(x) isn't an expression, and yet it *looks* like it should be,
and in the current syntax, an expression is required in that position.
To me, that suggests it would be hard to teach. [1]
You can of course generalise Sjoerd's "from" proposal and then just
replace "from" with "*" throughout. That avoids your requirement to
change append, but at the cost of the translation no longer being a
parallel to an existing use of "*".
Paul
[1] On a purely personal note, I'd say it's confusing, but I don't
want to go back to subjective arguments, so I only note that here as
an opinion, not an argument.
On 13 October 2016 at 21:47, אלעזר <ela...@gmail.com> wrote:
> if you allow result.append(1, 2, 3) to mean result.extend([1,2,3]) # which
> was discussed before
I don't (for the reasons raised before). But thank you for your
explanation, it clarifies what you were proposing. And it does so
within the *current* uses of the * symbol, which is good. But:
1. I'm not keen on extending append's meaning to overlap with extend's
like this.
2. Your proposal does not generalise to generator expressions, set
displays (without similarly modifying the set.add() method) or
dictionary displays.
3. *fn(x) isn't an expression, and yet it *looks* like it should be,
and in the current syntax, an expression is required in that position.
To me, that suggests it would be hard to teach. [1]
You can of course generalise Sjoerd's "from" proposal and then just
replace "from" with "*" throughout. That avoids your requirement to
change append, but at the cost of the translation no longer being a
parallel to an existing use of "*".
Regarding Steven's example, like Sven, I also see it this way:[*t for t in [(1, 'a'), (2, 'b'), (3, 'c')]]should mean:[*(1, 'a'), *(2, 'b'), *(3, 'c')]]Which coincides with what the OP is asking for.
<snip>
From a CPython implementation standpoint, we specifically blocked this code path, and it is only a matter of unblocking it if we want to support this.
You wrote "Marttii" and he corrected it when he quoted you in his reply.
I've never used nor taught a * in a list display. I don't think they seem so bad, but it's a step down a slippery slope towards forms that might as well be Perl.
I think that was actually discussed back when yield-from
was being thrashed out, but as far as I remember we didn't
have * in list displays then, so the argument for it was
weaker. If we had, it might have been given more serious
consideration.
--
Greg
--
---
You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/ROYNN7a5VAc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to python-ideas...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Le 14/10/2016 à 00:08, אלעזר a écrit :
> Trying to restate the proposal, somewhat more formal following Random832
> and Paul's suggestion.
>
> I only speak about the single star.
> ---
>
> *The suggested change of syntax:*
>
> comprehension ::= starred_expression comp_for
>
> *Semantics:*
>
> (In the following, f(x) must always evaluate to an iterable)
>
> 1. List comprehension:
>
> result = [*f(x) for x in iterable if cond]
>
> Translates to
>
> result = []
> for x in iterable:
> if cond:
> result.extend(f(x))
>
> 2. Set comprehension:
>
> result = {*f(x) for x in iterable if cond}
>
> Translates to
>
> result = set()
> for x in iterable:
> if cond:
> result.update(f(x))
Please note that we already have a way to do those. E.G:
result = [*f(x) for x in iterable if cond]
can currently been expressed as:
>>> iterable = range(10)
>>> f = lambda x: [x] * x
>>> [y for x in iterable if x % 2 == 0 for y in f(x)]
[2, 2, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8]
Now I do like the new extension syntax. I find it more natural, and more
readable:
>>> [*f(x) for x in iterable if x % 2 == 0]
But it's not a missing feature, it's really just a (rather nice)
syntaxic improvement.
And the transformation of *t for the items of t (I don't care if it is a
real transformation in the implementation, or only a fictional
transformation) cannot work in a list comp. Let's make the number of
items of t explicit so we don't have to worry about variable item
counts:
[*t for t in iterable] # t has three items
[a, b, c for (a, b, c) in iterable]
That's a syntax error. To avoid the syntax error, we need parentheses:
[(a, b, c) for (a, b, c) in iterable]
and that's a no-op.
Parentheses do not a tuple make. Commas do.
1, 2, 3, # three-element tuple
1, 2, # two-element tuple
1, # one-element tuple
The only time that a tuple requires parens is when it's the empty tuple, ().
ChrisA
On Thu, Oct 13, 2016 at 01:30:45PM -0700, Neil Girdhar wrote:
> From a CPython implementation standpoint, we specifically blocked this code
> path, and it is only a matter of unblocking it if we want to support this.
I find that difficult to believe. The suggested change seems like it
should be much bigger than just removing a block. Can you point us to
the relevant code?
In any case, it isn't really the difficulty of implementation that is
being questioned. Many things are easy to implement, but we still
don't do them.
The real questions here are:
(1) Should we overload list comprehensions as sugar for a flatten()
function?
(2) If so, should we spell that [*t for t in iterable]?
Actually the answer to (1) should be "we already do". We just spell it:
[x for t in iterable for x in t]
--
Steve
_______________________________________________
Python-ideas mailing list
Python...@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/
--
---
You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/ROYNN7a5VAc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to python-ideas...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
On Sun, Oct 16, 2016 at 4:33 AM, אלעזר <ela...@gmail.com> wrote:
> You are confusing here two distinct roles of the parenthesis: disambiguation
> as in "(1 + 2) * 2", and tuple construction as in (1, 2, 3). This overload
> is the reason that (1) is not a 1-tuple and we must write (1,).
Parentheses do not a tuple make. Commas do.
1, 2, 3, # three-element tuple
1, 2, # two-element tuple
1, # one-element tuple
Square brackets create a list. I'm not sure what you're not understanding, here.
The comma does have other meanings in other contexts (list/dict/set
display, function parameters), but outside of those, it means "create
tuple".
On Sun, Oct 16, 2016 at 4:38 AM, אלעזר <ela...@gmail.com> wrote:
> On Sat, Oct 15, 2016 at 8:36 PM Chris Angelico <ros...@gmail.com> wrote:
>>
>> On Sun, Oct 16, 2016 at 4:33 AM, אלעזר <ela...@gmail.com> wrote:
>> > You are confusing here two distinct roles of the parenthesis:
>> > disambiguation
>> > as in "(1 + 2) * 2", and tuple construction as in (1, 2, 3). This
>> > overload
>> > is the reason that (1) is not a 1-tuple and we must write (1,).
>>
>> Parentheses do not a tuple make. Commas do.
>>
>> 1, 2, 3, # three-element tuple
>> 1, 2, # two-element tuple
>> 1, # one-element tuple
>>
> And what [1, 2, 3] means? It's very different from [(1,2,3)].
>
> Python explicitly allow 1, 2, 3 to mean tuple in certain contexts, I agree.
>
Square brackets create a list. I'm not sure what you're not understanding, here.
The comma does have other meanings in other contexts (list/dict/set
display, function parameters), but outside of those, it means "create
tuple".
Or to disambiguate a tuple from some other comma-separated syntax. Hence
why you need the parens here:
[(b, a) for a,b in sequence]
--
Steve
On Oct 15, 2016 6:42 PM, "Steven D'Aprano" <st...@pearwood.info> wrote:
> doesn't make sense, it is invalid. Call it something else: the new
> "flatten" operator:
>
> [^t for t in iterable]
>
> for example, which magically adds an second invisible for-loop to your list comps:
This thread is a lot of work to try to save 8 characters in the spelling of `flatten(it)`. Let's just use the obvious and intuitive spelling.
We really don't need to be Perl. Folks who want to write Perl have a perfectly good interpreter available already.
The recipes in itertools give a nice implementation:
def flatten(listOfLists):
"Flatten one level of nesting"
return chain.from_iterable(listOfLists)
Yes, in the same way that other operators can need to be
disambiguated. You need to say (1).bit_length() because otherwise "1."
will be misparsed. You need parens to say x = (yield 5) + 2, else it'd
yield 7. But that's not because a tuple fundamentally needs
parentheses.
ChrisA