Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Greg Ewing

MRAB wrote:
Some languages use '=' for assignment, others for equality, but do you 
know of a language that uses ':=' for equality' or '==' for assignment?


No, but the only sane reason to use "==" for equality testing
seems to be if you're already using "=" for something else.
So maybe we should just implement "from __future__ import pascal"
and be done with. :-)

--
Greg
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Timing for removing legacy Unicode APIs deprecated by PEP 393

2018-04-18 Thread INADA Naoki
>
> I suppose that many users will start porting to Python 3 only in 2020, after
> 2.7 EOL. After that time we shouldn't support compatibility with 2.7 and can
> start emitting deprecation warnings at runtime. After 1 or 2 releases after
> that we can make corresponding public API always failing and remove private
> API and data fields.
>

Python 3.8 is planned to be released at  2019-10-20.  It's just before 2.7 EOL.
My current thought is:

* In 3.8, we make sure deprecated API emits warning (compile time if possible,
  runtime for others).

* If the deprecation is adopted smoothly, drop them in 3.9 (Mid 2021).
Otherwise,
  removal is postponed to 3.10 (Late 2023).

>
> There are other functions which expect that data is aligned to sizeof(long)
> or 8 bytes.
>
> Siphash hashing is special because it is called not just for strings and
> bytes, but for memoryview, which doesn't guarantee any alignment.
>

Oh, I'm sad about hear that...

> Note that after removing the wchar_t* field the gap will not gone, because
> the size of the structure should be a multiple of the alignment of the first
> field (which is a pointer).

Of course, we need hack for packing.

-- 
INADA Naoki  
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Antoine Pitrou

Agreed with Paul and Steve.  A lot of work seems to have gone into the
PEP (congratulations for that), but still the feature brings little to
no additional power to the language while making it more complex.

-1 from me.

Regards

Antoine.


On Tue, 17 Apr 2018 06:55:58 -0700
Steve Dower  wrote:
> Agree with Paul. The PEP is well thought out and well presented, but I really 
> don’t think we need this in Python (and I say this as someone who uses it 
> regularly in C/C#).
> 
> -1 on the idea; no disrespect intended toward to people who did a lot of work 
> on it.
> 
> Top-posted from my Windows phone
> 
> From: Paul Moore
> Sent: Tuesday, April 17, 2018 6:31
> To: David Mertz
> Cc: Nick Coghlan; Python-Dev
> Subject: Re: [Python-Dev] PEP 572: Assignment Expressions
> 
> On 17 April 2018 at 14:07, Paul Moore  wrote:
> > On 17 April 2018 at 14:01, David Mertz  wrote:  
> >> Strongly agree with Nick that only simple name targets should be permitted
> >> (at least initially). NONE of the motivating cases use more complex 
> >> targets,
> >> and allowing them encourages obscurity and code golf.  
> >
> > I also agree. Originally I would have said why not allow them, it's a
> > potentially useful generalisation. But Nick's examples pretty clearly
> > demonstrate that there are a lot of unclear edge cases involved, and
> > even though "prevent people writing ugly code" is explicitly stated as
> > a non-goal in the PEP, that doesn't mean it's OK to allow an obvious
> > bug magnet with no clear use cases.  
> 
> I should also point out that I remain -0 on this proposal (I'd already
> said this on python-ideas, but I should probably repeat it here). For
> me, the use cases are mostly marginal, and the major disadvantage is
> in having two forms of assignment. Explaining to a beginner why we use
> a := b in an expression, but a = b in a statement is going to be a
> challenge.
> 
> The fact that the PEP needs a section covering all the style guide
> warnings we feel are needed seems like it's a warning bell, too.
> 
> Paul
> ___
> Python-Dev mailing list
> Python-Dev@python.org
> https://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe: 
> https://mail.python.org/mailman/options/python-dev/steve.dower%40python.org
> 
> 



___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Oleg Broytman
On Wed, Apr 18, 2018 at 09:26:17PM +, "Gregory P. Smith"  
wrote:
> On Wed, Apr 18, 2018 at 11:04 AM Barry Warsaw  wrote:
> > Since I can???t have ???>>??? or ???<>??? back, I propose ???===???.
> 
> 8 of course.  to "match" what merge conflict markers look like. ;)

   Sorry for being pedantic, but git conflict markers are 7 in length.

> -gps

Oleg.
-- 
 Oleg Broytmanhttp://phdru.name/p...@phdru.name
   Programmers don't die, they just GOSUB without RETURN.
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Guido van Rossum
On Wed, Apr 18, 2018 at 11:17 AM, Chris Angelico  wrote:

> On Thu, Apr 19, 2018 at 2:18 AM, Guido van Rossum 
> wrote:
> > On Wed, Apr 18, 2018 at 7:35 AM, Chris Angelico 
> wrote:
> >>
> >> On Wed, Apr 18, 2018 at 11:58 PM, Guido van Rossum 
> >> wrote:
> >> > I can't tell from this what the PEP actually says should happen in
> that
> >> > example. When I first saw it I thought "Gaah! What a horrible piece of
> >> > code." But it works today, and people's code *will* break if we change
> >> > its
> >> > meaning.
> >> >
> >> > However we won't have to break that. Suppose the code is (perversely)
> >> >
> >> > t = range(3)
> >> > a = [t for t in t if t]
> >> >
> >> > If we translate this to
> >> >
> >> > t = range(3)
> >> > def listcomp(t=t):
> >> > a = []
> >> > for t in t:
> >> > if t:
> >> > a.append(t)
> >> > return a
> >> > a = listcomp()
> >> >
> >> > Then it will still work. The trick will be to recognize "imported"
> names
> >> > that are also assigned and capture those (as well as other captures as
> >> > already described in the PEP).
> >>
> >> That can be done. However, this form of importing will have one of two
> >> consequences:
> >>
> >> 1) Referencing an unbound name will scan to outer scopes at run time,
> >> changing the semantics of Python name lookups
> >
> >
> > I'm not even sure what this would do.
>
> The implicit function of the listcomp would attempt to LOAD_FAST 't',
> and upon finding that it doesn't have a value for it, would go and
> look for the name 't' in a surrounding scope. (Probably LOAD_CLOSURE.)
>

We agree that that's too dynamic to be explainable.


> >> 2) Genexps will eagerly evaluate a lookup if it happens to be the same
> >> name as an internal iteration variable.
> >
> >
> > I think we would have to specify this more precisely.
> >
> > Let's say by "eagerly evaluate a lookup" you mean "include it in the
> > function parameters with a default value being the lookup (i.e. starting
> in
> > the outer scope), IOW "t=t" as I showed above.
>
> Yes. To be technically precise, there's no default argument involved,
> and the call to the implicit function explicitly passes all the
> arguments.
>

OK, and the idea is the same -- it's explicitly evaluated in the outer
scope either way.


> > The question is *when* we
> > would do this. IIUC the PEP already does this if the "outer scope" is a
> > class scope for any names that a simple static analysis shows are
> references
> > to variables in the class scope.
>
> Correct.
>
> > (I don't know exactly what this static
> > analysis should do but it could be as simple as gathering all names that
> are
> > assigned to in the class, or alternatively all names assigned to before
> the
> > point where the comprehension occurs. We shouldn't be distracted by
> dynamic
> > definitions like `exec()` although we should perhaps be aware of `del`.)
>
> At the moment, it isn't aware of 'del'. The analysis is simple and
> 100% static: If a name is in the table of names the class uses AND
> it's in the table of names the comprehension uses, it gets passed as a
> parameter. I don't want to try to be aware of del, because of this:
>
> class X:
> x = 1
> if y: del x
> print(x)
> z = (q for q in x if q)
>
> If y is true, this will eagerly look up x using the same semantics in
> both the print and the genexp (on construction, not when you iterate
> over the genexp). If y is false, it'll still eagerly look up x, and
> it'll still use the same semantics for print and the genexp (and it'll
> find an 'x' in a surrounding scope).
>
> (The current implementation actually is a bit different from that. I'm
> not sure whether it's possible to do it as simply as given without an
> extra compilation pass. But it's close enough.)
>

Yeah, I threw 'del' in there mostly so we wouldn't get *too* confident. I
see a fair amount of this:

  d = {}
  for x, y in blah():
  d[x] = y
  del x, y


> > My proposal is to extend this static analysis for certain loop control
> > variables (any simple name assigned to in a for-clause in the
> > comprehension), regardless of what kind of scope the outer scope is. If
> the
> > outer scope is a function we already know how to do this. If it's a
> class we
> > use the analysis referred to above. If the outer scope is the global
> scope
> > we have to do something new. I propose to use the same simple static
> > analysis we use for class scopes.
> >
> > Furthermore I propose to *only* do this for the loop control variable(s)
> of
> > the outermost for-clause, since that's the only place where without all
> this
> > rigmarole we would have a clear difference in behavior with Python 3.7 in
> > cases like [t for t in t]. Oh, and probably we only need to do this if
> that
> > loop control variable is also used as an expression in the iterable (so
> we
> > don't waste time doing any of this for e.g. [t for t in q]).
>
> Okay. Here's something that would be doable:
>
> If the name is 

Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Gregory P. Smith
On Wed, Apr 18, 2018 at 11:04 AM Barry Warsaw  wrote:

> On Apr 18, 2018, at 10:43, MRAB  wrote:
>
> > Some languages use '=' for assignment, others for equality, but do you
> know of a language that uses ':=' for equality' or '==' for assignment?
>
> Clearly we should take a page from the ternary operator and make the
> assignment expression operator just ugly enough that people won’t overuse
> it.  Since I can’t have ‘>>’ or ‘<>’ back, I propose ‘===‘.
>
go-ahead-count-‘em-every-time-ly y’rs,
>

8 of course.  to "match" what merge conflict markers look like. ;)

php already uses === for something, we should just use === so we
can say "it goes to eleven", ending the operator war once and for all. :P

-gps
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Chris Angelico
On Thu, Apr 19, 2018 at 6:26 AM, Barry Warsaw  wrote:
> On Apr 18, 2018, at 11:17, Chris Angelico  wrote:
>
>> At the moment, it isn't aware of 'del’.
>
> I don’t know if it’s relevant to the current discussion, but don’t forget 
> about implicit dels:
>
> def foo():
> x = 1
> try:
> 1/0
> except ZeroDivisionError as x:
> pass
> print(x)
>
>
> This is one of my favorite Python oddities because it always makes me look 
> like a genius when I diagnose it. :)

Heh, yeah. My intention is to ignore that altogether. The general
policy in Python is "if ever it MIGHT be assigned to, it belongs to
that scope" (so even "if 0: x = 1" will mark x as local), so sticking
to that would mean treating x as local regardless of the try/except.

(On an unrelated subject, I'm keeping the "sublocal scope" concept
from the original PEP on ice. It might be worth implementing exception
name binding with a sublocal name. But that's for a completely
separate PEP.)

ChrisA
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Barry Warsaw
On Apr 18, 2018, at 11:17, Chris Angelico  wrote:

> At the moment, it isn't aware of 'del’.

I don’t know if it’s relevant to the current discussion, but don’t forget about 
implicit dels:

def foo():
x = 1
try:
1/0
except ZeroDivisionError as x:
pass
print(x)


This is one of my favorite Python oddities because it always makes me look like 
a genius when I diagnose it. :)

-Barry



signature.asc
Description: Message signed with OpenPGP
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Timing for removing legacy Unicode APIs deprecated by PEP 393

2018-04-18 Thread Serhiy Storchaka

13.04.18 16:27, INADA Naoki пише:

Then, I want to reschedule the removal of these APIs.
Can we remove them in 3.8? 3.9? or 3.10?
I prefer sooner as possible.


I suppose that many users will start porting to Python 3 only in 2020, 
after 2.7 EOL. After that time we shouldn't support compatibility with 
2.7 and can start emitting deprecation warnings at runtime. After 1 or 2 
releases after that we can make corresponding public API always failing 
and remove private API and data fields.



Slightly off topic, there are 4bytes alignment gap in the unicode object,
on 64bit platform.

typedef struct {
.
 struct {
 unsigned int interned:2;
 unsigned int kind:3;
 unsigned int compact:1;
 unsigned int ascii:1;
 unsigned int ready:1;
 unsigned int :24;
 } state;  // 4 bytes

 // implicit 4 bytes gap here.

 wchar_t *wstr;  // 8 bytes
} PyASCIIObject;

So, I think we can reduce 12 bytes instead of 8 bytes when removing wstr.
Or we can reduce 4 bytes soon by moving `wstr` before `state`.

Off course, it needs siphash support 4byte aligned data instead of 8byte.


There are other functions which expect that data is aligned to 
sizeof(long) or 8 bytes.


Siphash hashing is special because it is called not just for strings and 
bytes, but for memoryview, which doesn't guarantee any alignment.


Note that after removing the wchar_t* field the gap will not gone, 
because the size of the structure should be a multiple of the alignment 
of the first field (which is a pointer).


___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Chris Angelico
On Thu, Apr 19, 2018 at 2:18 AM, Guido van Rossum  wrote:
> On Wed, Apr 18, 2018 at 7:35 AM, Chris Angelico  wrote:
>>
>> On Wed, Apr 18, 2018 at 11:58 PM, Guido van Rossum 
>> wrote:
>> > I can't tell from this what the PEP actually says should happen in that
>> > example. When I first saw it I thought "Gaah! What a horrible piece of
>> > code." But it works today, and people's code *will* break if we change
>> > its
>> > meaning.
>> >
>> > However we won't have to break that. Suppose the code is (perversely)
>> >
>> > t = range(3)
>> > a = [t for t in t if t]
>> >
>> > If we translate this to
>> >
>> > t = range(3)
>> > def listcomp(t=t):
>> > a = []
>> > for t in t:
>> > if t:
>> > a.append(t)
>> > return a
>> > a = listcomp()
>> >
>> > Then it will still work. The trick will be to recognize "imported" names
>> > that are also assigned and capture those (as well as other captures as
>> > already described in the PEP).
>>
>> That can be done. However, this form of importing will have one of two
>> consequences:
>>
>> 1) Referencing an unbound name will scan to outer scopes at run time,
>> changing the semantics of Python name lookups
>
>
> I'm not even sure what this would do.

The implicit function of the listcomp would attempt to LOAD_FAST 't',
and upon finding that it doesn't have a value for it, would go and
look for the name 't' in a surrounding scope. (Probably LOAD_CLOSURE.)

>> 2) Genexps will eagerly evaluate a lookup if it happens to be the same
>> name as an internal iteration variable.
>
>
> I think we would have to specify this more precisely.
>
> Let's say by "eagerly evaluate a lookup" you mean "include it in the
> function parameters with a default value being the lookup (i.e. starting in
> the outer scope), IOW "t=t" as I showed above.

Yes. To be technically precise, there's no default argument involved,
and the call to the implicit function explicitly passes all the
arguments.

> The question is *when* we
> would do this. IIUC the PEP already does this if the "outer scope" is a
> class scope for any names that a simple static analysis shows are references
> to variables in the class scope.

Correct.

> (I don't know exactly what this static
> analysis should do but it could be as simple as gathering all names that are
> assigned to in the class, or alternatively all names assigned to before the
> point where the comprehension occurs. We shouldn't be distracted by dynamic
> definitions like `exec()` although we should perhaps be aware of `del`.)

At the moment, it isn't aware of 'del'. The analysis is simple and
100% static: If a name is in the table of names the class uses AND
it's in the table of names the comprehension uses, it gets passed as a
parameter. I don't want to try to be aware of del, because of this:

class X:
x = 1
if y: del x
print(x)
z = (q for q in x if q)

If y is true, this will eagerly look up x using the same semantics in
both the print and the genexp (on construction, not when you iterate
over the genexp). If y is false, it'll still eagerly look up x, and
it'll still use the same semantics for print and the genexp (and it'll
find an 'x' in a surrounding scope).

(The current implementation actually is a bit different from that. I'm
not sure whether it's possible to do it as simply as given without an
extra compilation pass. But it's close enough.)

> My proposal is to extend this static analysis for certain loop control
> variables (any simple name assigned to in a for-clause in the
> comprehension), regardless of what kind of scope the outer scope is. If the
> outer scope is a function we already know how to do this. If it's a class we
> use the analysis referred to above. If the outer scope is the global scope
> we have to do something new. I propose to use the same simple static
> analysis we use for class scopes.
>
> Furthermore I propose to *only* do this for the loop control variable(s) of
> the outermost for-clause, since that's the only place where without all this
> rigmarole we would have a clear difference in behavior with Python 3.7 in
> cases like [t for t in t]. Oh, and probably we only need to do this if that
> loop control variable is also used as an expression in the iterable (so we
> don't waste time doing any of this for e.g. [t for t in q]).

Okay. Here's something that would be doable:

If the name is written to within the comprehension, AND it is read
from in the outermost iterable, it is flagged early-bind.

I'll have to try implementing that to be sure, but it should be
possible I think. It would cover a lot of cases, keeping them the same
as we currently have.

> Since we now have once again introduced an exception for the outermost loop
> control variable and the outermost iterable, we can consider doing this only
> as a temporary measure. We could have a goal to eventually make [t for t in
> t] fail, and in the meantime we would deprecate it -- e.g. in 3.8 a silent
> deprecation, in 3.9 a n

Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Barry Warsaw
On Apr 18, 2018, at 10:43, MRAB  wrote:

> Some languages use '=' for assignment, others for equality, but do you know 
> of a language that uses ':=' for equality' or '==' for assignment?

Clearly we should take a page from the ternary operator and make the assignment 
expression operator just ugly enough that people won’t overuse it.  Since I 
can’t have ‘>>’ or ‘<>’ back, I propose ‘===‘.

go-ahead-count-‘em-every-time-ly y’rs,
-Barry



signature.asc
Description: Message signed with OpenPGP
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread MRAB

On 2018-04-17 22:44, Greg Ewing wrote:

Paul Moore wrote:

the next question will likely be "so why does = exist at all?"


And if we decide to make ':=' the official assigment operator and
deprectate '=', the next question will be "Why do we have '=='
instead of '='?"

Some languages use '=' for assignment, others for equality, but do you 
know of a language that uses ':=' for equality' or '==' for assignment?


If Python saw '=' it could ask "Do you mean assignment ':=' or equality 
'=='?".

___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Tim Peters
[Tim]
>> And, for some reason, I find this even worse:
>>
>> while ((x, y) := func_returning_tuple())[1] is not None:
>> ...
>>
>> The rub there:  I gave `y` a name but can't use it in the test?!
>>
>> And those are the same kinds of headaches I saw over & over in my own
>> "fancier" code:  stuff that's already perfectly clear would become
>> more obscure instead.

[Nick]
> Whereas I think:
>
> while (s := func_returning_tuple())[1] is not None:
> s = x, y
> ...
>
> compares favourably with the loop-and-a-half version.

Obviously not, since it really needs to be

x, y = s

instead ;-)

In context, I was looking for realistic cases in which assignment
expressions _fancier than_

identifier ":=" expression

is a real improvement.  You found an improvement instead by
_replacing_ a "fancier than" instance with a plain-single-name target.
I already have lots of examples from real code where plain-single-name
target reads better to me.  I don't have any yet from real code where
something fancier does.

In this specific case, I find your rewriting about as readable as the
loop-and-a-half, except for the obvious drawback of the former:

> It does make the guarantee that "y is not None" harder to spot than it
> is in the loop-and-a-half version, though.

Over time, the functions in the real codes from which the example was
synthesized change, sometimes carrying more or less state in tuples.
When that happens, the original

x, y  = s

will helpfully blow up (size mismatch in unpacking),  But, if the
tuple length increased, is it still the case that I want to test the
1'th component?  The test is now divorced from the unpacking.  I do
know that I'll still want to test the component I think of as being
"the 'y' component", and the loop-and-a-half version accommodates that
naturally.

Then again, I could switch to new-fanged namedtuples instead, and do

while (s := func_returning_tuple()).y is not None:

to get the best of all worlds.
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Brett Cannon
On Tue, 17 Apr 2018 at 14:53 Eric Snow  wrote:

> On Tue, Apr 17, 2018 at 7:55 AM, Steve Dower 
> wrote:
> > Agree with Paul. The PEP is well thought out and well presented, but I
> > really don’t think we need this in Python (and I say this as someone who
> > uses it regularly in C/C#).
> >
> > -1 on the idea; no disrespect intended toward to people who did a lot of
> > work on it.
>
> Same here.  I'm more interested in having anonymous blocks (i.e.
> scopes), along the lines of PEP 3150, though it's currently #2 on my
> wish list. :)
>

I'm also -1.

I understand the usefulness of the construct in languages where block
scopes make having this kind of expression assignment in e.g. an 'if' guard
useful. But for Python and it's LGB scoping -- although I think we need to
add an "N" for "non-local" :) -- this is syntactic sugar and I don't see
enough wide benefit on top of the potential ugliness/misuse of it to
warrant the cognitive overhead of adding it.

Although, as usual, Chris, great PEP! :)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Guido van Rossum
On Wed, Apr 18, 2018 at 7:35 AM, Chris Angelico  wrote:

> On Wed, Apr 18, 2018 at 11:58 PM, Guido van Rossum 
> wrote:
> > I can't tell from this what the PEP actually says should happen in that
> > example. When I first saw it I thought "Gaah! What a horrible piece of
> > code." But it works today, and people's code *will* break if we change
> its
> > meaning.
> >
> > However we won't have to break that. Suppose the code is (perversely)
> >
> > t = range(3)
> > a = [t for t in t if t]
> >
> > If we translate this to
> >
> > t = range(3)
> > def listcomp(t=t):
> > a = []
> > for t in t:
> > if t:
> > a.append(t)
> > return a
> > a = listcomp()
> >
> > Then it will still work. The trick will be to recognize "imported" names
> > that are also assigned and capture those (as well as other captures as
> > already described in the PEP).
>
> That can be done. However, this form of importing will have one of two
> consequences:
>
> 1) Referencing an unbound name will scan to outer scopes at run time,
> changing the semantics of Python name lookups
>

I'm not even sure what this would do.


> 2) Genexps will eagerly evaluate a lookup if it happens to be the same
> name as an internal iteration variable.
>

I think we would have to specify this more precisely.

Let's say by "eagerly evaluate a lookup" you mean "include it in the
function parameters with a default value being the lookup (i.e. starting in
the outer scope), IOW "t=t" as I showed above. The question is *when* we
would do this. IIUC the PEP already does this if the "outer scope" is a
class scope for any names that a simple static analysis shows are
references to variables in the class scope. (I don't know exactly what this
static analysis should do but it could be as simple as gathering all names
that are assigned to in the class, or alternatively all names assigned to
before the point where the comprehension occurs. We shouldn't be distracted
by dynamic definitions like `exec()` although we should perhaps be aware of
`del`.)

My proposal is to extend this static analysis for certain loop control
variables (any simple name assigned to in a for-clause in the
comprehension), regardless of what kind of scope the outer scope is. If the
outer scope is a function we already know how to do this. If it's a class
we use the analysis referred to above. If the outer scope is the global
scope we have to do something new. I propose to use the same simple static
analysis we use for class scopes.

Furthermore I propose to *only* do this for the loop control variable(s) of
the outermost for-clause, since that's the only place where without all
this rigmarole we would have a clear difference in behavior with Python 3.7
in cases like [t for t in t]. Oh, and probably we only need to do this if
that loop control variable is also used as an expression in the iterable
(so we don't waste time doing any of this for e.g. [t for t in q]).

(But what about [t for _ in t for t in t]? That's currently an
UnboundLocalError and we shouldn't try to "fix" that case.)

Since we now have once again introduced an exception for the outermost loop
control variable and the outermost iterable, we can consider doing this
only as a temporary measure. We could have a goal to eventually make [t for
t in t] fail, and in the meantime we would deprecate it -- e.g. in 3.8 a
silent deprecation, in 3.9 a noisy one, in 3.10 break it. Yes, that's a lot
of new static analysis for deprecating an edge case, but it seems
reasonable to want to preserve backward compatibility when breaking this
edge case since it's likely not all that uncommon. Even if most occurrences
are bad style written by lazy programmers, we should not break working
code, if it is reasonable to expect that it's relied upon in real code.


> Of the two, #2 is definitely my preference, but it does mean more
> eager binding.While this won't make a difference in the outermost
> iterable (since that's *already* eagerly bound), it might make a
> difference with others:
>
> t = range(3)
> gen = (t for _ in range(1) for t in t if t)
> t = [4, 5, 6]
> print(next(gen))
> print(next(gen))
>

I don't like this particular example, because it uses an obscure bit of
semantics of generator expressions. It's fine to demonstrate the finer
details of how those work, but it's unlikely to see real code relying on
this. (As I argued before, generator expressions are typically either fed
into other code that eagerly evaluates them before reaching the next line,
or returned from a function, and in the latter case intentional
modification of some variable in that function's scope to affect the
meaning of the generator expression would seem a remote possibility at
best, and an accident waiting to happen at worst.)


> Current semantics: UnboundLocalError on first next() call.
>
> PEP 572 semantics: Either UnboundLocalError (with current reference
> implementation) or it yields 1 and 2 (with eager lookups).
>
> So either we change t

Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Chris Angelico
On Wed, Apr 18, 2018 at 11:58 PM, Guido van Rossum  wrote:
> I can't tell from this what the PEP actually says should happen in that
> example. When I first saw it I thought "Gaah! What a horrible piece of
> code." But it works today, and people's code *will* break if we change its
> meaning.
>
> However we won't have to break that. Suppose the code is (perversely)
>
> t = range(3)
> a = [t for t in t if t]
>
> If we translate this to
>
> t = range(3)
> def listcomp(t=t):
> a = []
> for t in t:
> if t:
> a.append(t)
> return a
> a = listcomp()
>
> Then it will still work. The trick will be to recognize "imported" names
> that are also assigned and capture those (as well as other captures as
> already described in the PEP).

That can be done. However, this form of importing will have one of two
consequences:

1) Referencing an unbound name will scan to outer scopes at run time,
changing the semantics of Python name lookups
2) Genexps will eagerly evaluate a lookup if it happens to be the same
name as an internal iteration variable.

Of the two, #2 is definitely my preference, but it does mean more
eager binding. While this won't make a difference in the outermost
iterable (since that's *already* eagerly bound), it might make a
difference with others:

t = range(3)
gen = (t for _ in range(1) for t in t if t)
t = [4, 5, 6]
print(next(gen))
print(next(gen))

Current semantics: UnboundLocalError on first next() call.

PEP 572 semantics: Either UnboundLocalError (with current reference
implementation) or it yields 1 and 2 (with eager lookups).

So either we change things for the outermost iterable, or we change
things for everything BUT the outermost iterable. Either way, I'm
happy to eliminate the special-casing of the outermost iterable. Yes,
it's a change in semantics, but a change that removes special cases is
generally better than one that creates them.

ChrisA
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Guido van Rossum
On Tue, Apr 17, 2018 at 7:04 PM, Chris Angelico  wrote:

> On Wed, Apr 18, 2018 at 11:20 AM, Steven D'Aprano 
> wrote:
> > On Wed, Apr 18, 2018 at 10:13:58AM +1000, Chris Angelico wrote:
> >
> > [regarding comprehensions]
> >
> >> The changes here are only to edge and corner cases, other than as they
> >> specifically relate to assignment expressions. The current behaviour
> >> is intended to "do the right thing" according to people's
> >> expectations, and it largely does so; those cases are not changing.
> >> For list comprehensions at global or function scope, the ONLY case
> >> that can change (to my knowledge) is where you reuse a variable name:
> >>
> >> [t for t in t.__parameters__ if t not in tvars]
> >>
> >> This works in 3.7 but will fail easily and noisily (UnboundLocalError)
> >> with PEP 572.
> >
> > That's a major semantic change, and the code you show is no better or
> > worse than:
> >
> > t = ...
> > result = []
> > for t in t.parameters:
> > if t not in tvars:
> > result.append(t)
> >
> >
> > which is allowed. I think you need a better justification for breaking
> > it than merely the opinion:
> >
> >> IMO this is a poor way to write a loop,
>
> Ah but that isn't what the list comp is equivalent to. If you want to
> claim that "for t in t.parameters" is legal, you first have to assume
> that you're overwriting t, not shadowing it. In the list comp as it is
> today, the "for t in" part is inside an implicit nested function, but
> the "t.parameters" part is outside that function.
>
> Try this instead:
>
> t = ...
> def listcomp():
> result = []
> for t in t.parameters:
> if t not in tvars:
> result.append(t)
> return result
> listcomp()
>
> Except that it isn't that either, because the scope isn't quite that
> clean. It actually involves a function parameter, and the iterator is
> fetched before it's passed as a parameter, and then NOT fetched inside
> the loop. So you actually can't write perfectly equivalent longhand.
>
> PEP 572 will *reduce* the edge cases and complexity.


I can't tell from this what the PEP actually says should happen in that
example. When I first saw it I thought "Gaah! What a horrible piece of
code." But it works today, and people's code *will* break if we change its
meaning.

However we won't have to break that. Suppose the code is (perversely)

t = range(3)
a = [t for t in t if t]

If we translate this to

t = range(3)
def listcomp(t=t):
a = []
for t in t:
if t:
a.append(t)
return a
a = listcomp()

Then it will still work. The trick will be to recognize "imported" names
that are also assigned and capture those (as well as other captures as
already described in the PEP).

-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Nick Coghlan
On 18 April 2018 at 10:20, Guido van Rossum  wrote:
[Tim Peters wrote]
>> 2. Back off and limit assignment expressions to what appears to be the
>> overwhelmingly most common case motivated by looking at real code (as
>> opposed to constructing examples to illustrate pitfalls &
>> obscurities):
>>
>> identifier ":=" expression
>
>
> I haven't had the time to follow this thread in detail; fortunately I don't
> have to because of Tim's excellent channeling.
>
> I am fine with this, it certainly seems the easiest to implement, with the
> fewest corner cases, and the easiest restriction to explain.
>
> (I was thinking there would be a use case for basic tuple unpacking, like
> seen a lot in for-loop, but the only examples I tried to come up with were
> pretty sub-optimal, so I don't worry about that any more.)

In the other direction I was thinking about the question "Then why do
I think tuple unpacking is OK in comprehensions?", and realised that
it's because in that situation there are keywords as delimiters on
both sides (i.e. "... for name [, name]* in ..."), so it's harder for
the unpacking operation to get confused with other uses of commas as
separators. Similarly, in regular assignments, the unpacking target is
always either between two "=" or else from the start of the line to
the first "=".

By contrast, for assignment expressions, the only potential explicit
opening delimiter is "(", and that's also the case for tuple literals.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Nick Coghlan
On 18 April 2018 at 11:35, Tim Peters  wrote:
> And, for some reason, I find this even worse:
>
> while ((x, y) := func_returning_tuple())[1] is not None:
> ...
>
> The rub there:  I gave `y` a name but can't use it in the test?!
>
> And those are the same kinds of headaches I saw over & over in my own
> "fancier" code:  stuff that's already perfectly clear would become
> more obscure instead.

Whereas I think:

while (s := func_returning_tuple())[1] is not None:
s = x, y
...

compares favourably with the loop-and-a-half version.

It does make the guarantee that "y is not None" harder to spot than it
is in the loop-and-a-half version, though.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-18 Thread Nick Coghlan
On 18 April 2018 at 15:49, Jeroen Demeyer  wrote:
> On 2018-04-18 02:13, Chris Angelico wrote:
>>
>> I'm much happier promoting a full-featured assignment expression than
>> something that can only be used in a limited set of situations. Is
>> there reason to believe that extensions to the := operator might take
>> it in a different direction? If not, there's very little to lose by
>> permitting any assignment target, and then letting style guides frown
>> on it if they like.
>
>
> This is a very good argument: why artificially restrict the operator?
>
> This reminds me of the artificial restriction of decorator syntax (why is
> @foo()() not legal?). There was never a rationale given for that and now we
> are stuck with it.

That's not the case - Guido's given in-principle approval for the
decorator restriction to be lifted (I believe primarily due to the
validity of the "@ns[lookup]" case), but it's never irritated anyone
enough for them to do the work to propose the necessary grammar and
reference implementation changes (vs using a pass-through function
like "@decorator(ns[lookup])" to bypass the restriction).

So what these kinds of initial restrictions do is require that any
extra complexity be justified *in its own right*, rather than coming
along for the ride as part of proposals with motivating use cases that
only require much simpler capabilities (names and calls in the case of
decorators, names in the case of assignment expressions).

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com