Re: [Python-Dev] The `for y in [x]` idiom in comprehensions

2018-02-26 Thread Nick Coghlan
On 26 February 2018 at 01:08, Chris Angelico  wrote:

> Speaking as a C programmer who's quite happy to write code like "while
> ((var = func()) != sentinel)", I wouldn't object to this coming up in
> Python; the "as name" syntax has the huge advantage over C's syntax in
> that you can't accidentally leave off one equals sign and get the
> wrong behaviour. But I know that a lot of people dislike this at a
> more fundamental level.
>
> If someone wants to push for this, I think it probably needs a PEP -
> it's a point that comes up periodically. I don't think it's ever had a
> PEP written about it, but it's a bit hard to search for; maybe someone
> else knows off hand?
>

PEP 3150 is the most closely related PEP we have at the moment (and that
only works for simple statements, since it relies on using a trailing block
to name the subexpressions).

The "(EXPR as NAME)" syntax comes up periodically, especially in the
context of while loops (where it would allow a direct translation of
C-style embedded assignment idioms).

In addition to the potential confusion for "with (EXPR as NAME):" vs "with
EXPR as NAME:" (and the similar ambiguity for "except" clauses), some other
major questions to be resolved are:

* are statement locals in a class namespace turned into attributes on the
resulting class? (it would be more useful if they weren't)
* are statement locals in a module namespace turned into attributes on the
resulting module? (it would be more useful if they weren't)
* are statement locals in a function/generator/coroutine namespace kept
alive until the entire call terminates? (it would be more useful if they
weren't)
* do currently defined statement locals appear in calls to locals() or in
frame.f_locals?
* can lexically nested scopes see names bound this way? (a lot of complex
name resolution problems disappear if they can't, plus you get a clearer
distinction between these and regular function locals)

To be interesting enough to potentially be worthy of syntax, I think name
bindings written this way would need to be truly statement local:

* reference is released at the end of the statement (whether simple or
compound)
* no ability to close over them (this goes hand in hand with eagerly
dropping the reference)
* we play name mangling games and/or use different opcodes to avoid
overwriting regular function locals and to avoid appearing in locals()

That's still only enough to get the concept into python-ideas territory
though (as per the discussion of "(EXPR as .NAME)" in
https://mail.python.org/pipermail/python-ideas/2018-February/049002.html) -
it's still a *long* way from being fully baked enough to make into a
concrete change proposal for 3.8+.

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] The `for y in [x]` idiom in comprehensions

2018-02-26 Thread Chris Angelico
On Mon, Feb 26, 2018 at 8:00 PM, Nick Coghlan  wrote:
> On 26 February 2018 at 01:08, Chris Angelico  wrote:
>>
>> Speaking as a C programmer who's quite happy to write code like "while
>> ((var = func()) != sentinel)", I wouldn't object to this coming up in
>> Python; the "as name" syntax has the huge advantage over C's syntax in
>> that you can't accidentally leave off one equals sign and get the
>> wrong behaviour. But I know that a lot of people dislike this at a
>> more fundamental level.
>>
>> If someone wants to push for this, I think it probably needs a PEP -
>> it's a point that comes up periodically. I don't think it's ever had a
>> PEP written about it, but it's a bit hard to search for; maybe someone
>> else knows off hand?
>
>
> PEP 3150 is the most closely related PEP we have at the moment (and that
> only works for simple statements, since it relies on using a trailing block
> to name the subexpressions).
>
> The "(EXPR as NAME)" syntax comes up periodically, especially in the context
> of while loops (where it would allow a direct translation of C-style
> embedded assignment idioms).
>
> In addition to the potential confusion for "with (EXPR as NAME):" vs "with
> EXPR as NAME:" (and the similar ambiguity for "except" clauses), some other
> major questions to be resolved are:
>
> * are statement locals in a class namespace turned into attributes on the
> resulting class? (it would be more useful if they weren't)
> * are statement locals in a module namespace turned into attributes on the
> resulting module? (it would be more useful if they weren't)
> * are statement locals in a function/generator/coroutine namespace kept
> alive until the entire call terminates? (it would be more useful if they
> weren't)
> * do currently defined statement locals appear in calls to locals() or in
> frame.f_locals?
> * can lexically nested scopes see names bound this way? (a lot of complex
> name resolution problems disappear if they can't, plus you get a clearer
> distinction between these and regular function locals)
>
> To be interesting enough to potentially be worthy of syntax, I think name
> bindings written this way would need to be truly statement local:
>
> * reference is released at the end of the statement (whether simple or
> compound)
> * no ability to close over them (this goes hand in hand with eagerly
> dropping the reference)
> * we play name mangling games and/or use different opcodes to avoid
> overwriting regular function locals and to avoid appearing in locals()

Definitely possible. I wish I still had the POC patch that I put
together a while ago that created block-local variables from 'as'
bindings in except and with statements, but it got lost in a hard
drive crash (because, at the time, I didn't think it was useful for
anything more than "haha, isn't that cute"). Will see if I can
recreate it.

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] The `for y in [x]` idiom in comprehensions

2018-02-26 Thread Stephen J. Turnbull
Michel Desmoulin writes:

 > Le 25/02/2018 à 14:11, Nikolaus Rath a écrit :

>>> result = [ (f(x) as y) + g(y) for x in range(10)]

 > Honestly I find this version the most readable while the double for
 > loop is completely weird to me, despite doing python for a living
 > for years.

I find this one less readable because I don't expect name binding
syntax to return a value.  My brain is nonplussed by the "+". :-)

 > I really hope the later doesn't become a common idiom.

It already is common, for values of "common" = "some people have been
using it where it's useful, but it's not useful all that often".  I
suppose it's rare because it's a less-than-readable optimization,
which is frowned on in Python programming.

Somebody counted four or five ways to perform this optimization,
including this "double for" that allows the common subexpression
optimization to be made explicit in the comprehension.  We don't need
another, not at the cost of new syntax.

If we find that we really want a C-like assignment expression that
returns the assigned value[1], I don't have an objection to that.  But
I don't personally feel a need for it.

Footnotes: 
[1]  Yes, I know that technically it's a local name binding, not an
assignment.  But until the scope of "local" is resolved, it looks,
smells, and tastes like an assignment.

___
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


[Python-Dev] Code stability and a big thank you

2018-02-26 Thread Chris Angelico
As part of the discussions on local name binding, I dug up a patch
that I sent to python-ideas back in June 2015. That's two and a half
years ago, and was based on CPython 3.5. Here's what happened when I
attempted to apply it to the current master branch (3.8):

$ patch <~/Downloads/scope_hack.patch -p1
patching file Python/compile.c
Hunk #1 succeeded at 100 (offset 3 lines).
Hunk #2 succeeded at 134 (offset 3 lines).
Hunk #3 succeeded at 793 (offset 2 lines).
Hunk #4 succeeded at 2824 (offset 432 lines).
Hunk #5 succeeded at 2871 with fuzz 2 (offset 430 lines).
Hunk #6 succeeded at 3312 (offset 402 lines).
Hunk #7 succeeded at 3330 (offset 402 lines).
Hunk #8 succeeded at 5442 with fuzz 1 (offset 831 lines).
Hunk #9 succeeded at 5457 with fuzz 2 (offset 832 lines).
Hunk #10 succeeded at 5520 with fuzz 2 (offset 823 lines).
$

And the build ran fine, the test suite didn't gain any new failures
(there's no tests for the new functionality, but it didn't break
anything else), and my original demo program worked just fine.

Thank you to all the CPython core devs for maintaining code that is
THIS stable. Stuff happens but there's no unnecessary code churn.
Sure, GNU Patch had to do a bit of work to merge in the changes, but
it did it completely automatically. You folks are awesome!

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] Should the dataclass frozen property apply to subclasses?

2018-02-26 Thread Eric V. Smith

I've opened https://bugs.python.org/issue32953 to track this.

On 2/22/18 5:55 AM, Eric V. Smith wrote:

On 2/22/2018 1:56 AM, Raymond Hettinger wrote:

When working on the docs for dataclasses, something unexpected came
up.  If a dataclass is specified to be frozen, that characteristic is
inherited by subclasses which prevents them from assigning additional
attributes:

 >>> @dataclass(frozen=True)
 class D:
 x: int = 10

 >>> class S(D):
 pass

 >>> s = S()
 >>> s.cached = True
 Traceback (most recent call last):
   File "", line 1, in 
 s.cached = True
   File
"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/dataclasses.py",
line 448, in _frozen_setattr
 raise FrozenInstanceError(f'cannot assign to field {name!r}')
 dataclasses.FrozenInstanceError: cannot assign to field 'cached'


This is because "frozen-ness" is implemented by adding __setattr__ and
__delattr__ methods in D, which get inherited by S.



...


A related issue is that dataclasses derived from frozen dataclasses are
automatically "promoted" to being frozen.


@dataclass(frozen=True)

... class A:
... i: int
...

@dataclass

... class B(A):
... j: int
...

b = B(1, 2)
b.j = 3

Traceback (most recent call last):
  File "", line 1, in 
  File "C:\home\eric\local\python\cpython\lib\dataclasses.py", line 452,
in _frozen_setattr
raise FrozenInstanceError(f'cannot assign to field {name!r}')
dataclasses.FrozenInstanceError: cannot assign to field 'j'


This is tricky to fix.

Here's the problem with a inheriting a non-frozen dataclass from a 
frozen one. Consider class Y in this example:


@dataclass
class X:
x: int

@dataclass
class Y(X):
y: int

Y's __init__ looks like:

def __init__(self, x, y):
self.x = x
self.y = y

That is, all of the initializing for Y and its base classes which are 
dataclasses is done in Y's __init__. There are a number of reasons for 
this, including performance and not knowing how to call the base 
classes' __init__ methods.


If Y is frozen, then the __init__ currently looks like:

def __init__(self, x, y):
object.__setattr__(self, 'x', x)
object.__setattr__(self, 'y', y)

If X is frozen but Y isn't, then it should look like:

def __init__(self, x, y):
object.__setattr__(self, 'x', x)
self.y = y

But I currently can't generate the code that way, because I don't know 
if a base dataclass is frozen. That information is not saved on a dataclass.


I think the right thing to do is to record with each dataclass if it is 
frozen or not, so that derived classes can generate the correct code. 
Another option would be to always use object.__setattr__, but that will 
hurt performance in the common case.


As long as I'm saving if a dataclass is frozen, I should save all of the 
dataclass parameters on the class. Since it's per-class, it's not a lot 
of overhead.


I should probably separate the two issues here: 1) deriving a 
non-dataclass from a frozen dataclass and 2) deriving a non-frozen 
dataclass from a frozen dataclass, but since they're somewhat related 
I'm going to keep them together for the time being. #1 was Raymond's 
initial report.


Eric
___
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] Should the dataclass frozen property apply to subclasses?

2018-02-26 Thread Eric V. Smith

On 2/22/18 9:43 PM, Nick Coghlan wrote:

On 22 February 2018 at 20:55, Eric V. Smith  wrote:

On 2/22/2018 1:56 AM, Raymond Hettinger wrote:

When working on the docs for dataclasses, something unexpected came up.
If a dataclass is specified to be frozen, that characteristic is inherited
by subclasses which prevents them from assigning additional attributes:

  >>> @dataclass(frozen=True)
  class D:
  x: int = 10

  >>> class S(D):
  pass

  >>> s = S()
  >>> s.cached = True
  Traceback (most recent call last):
File "", line 1, in 
  s.cached = True
File
"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/dataclasses.py",
line 448, in _frozen_setattr
  raise FrozenInstanceError(f'cannot assign to field {name!r}')
  dataclasses.FrozenInstanceError: cannot assign to field 'cached'


This is because "frozen-ness" is implemented by adding __setattr__ and
__delattr__ methods in D, which get inherited by S.


Other immutable classes in Python don't behave the same way:


  >>> class T(tuple):
  pass

  >>> t = T([10, 20, 30])
  >>> t.cached = True

  >>> class F(frozenset):
  pass

  >>> f = F([10, 20, 30])
  >>> f.cached = True

  >>> class B(bytes):
  pass

  >>> b = B()
  >>> b.cached = True


The only way I can think of emulating this is checking in __setattr__ to see
if the field name is a field of the frozen class, and only raising an error
in that case.

If you were going to do that then it would likely make more sense to
convert the frozen fields to data descriptors, so __setattr__ only
gets called for attempts to add new attributes.

Then for the `frozen=False` case, the decorator could force
__setattr__ and __delattr__ back to the default implementations from
object, rather than relying on the behaviour inherited from base
classes.


I guess it depends on what we're trying to do. By using data 
descriptors, we'd be saying "the fields can't be modified, but other 
parts of the class can". For example, what should happen here:


@dataclass(frozen=True)
class F:
   i: int

f = F(10)
f.i = 0   # should be an error
f.j = 0   # should this be an error? It's not a field.

I was hoping to get all of this finished today, by a2, but it looks like 
that's not going to happen.

A related issue is that dataclasses derived from frozen dataclasses are
automatically "promoted" to being frozen.


@dataclass(frozen=True)

... class A:
... i: int
...

@dataclass

... class B(A):
... j: int
...

b = B(1, 2)
b.j = 3

Traceback (most recent call last):
   File "", line 1, in 
   File "C:\home\eric\local\python\cpython\lib\dataclasses.py", line 452, in
_frozen_setattr
 raise FrozenInstanceError(f'cannot assign to field {name!r}')
dataclasses.FrozenInstanceError: cannot assign to field 'j'

Maybe it should be an error to declare B as non-frozen?

It would be nice to avoid that, as a mutable subclass of a frozen base
class could be a nice way to model hashable-but-mutable types:

 >>> @dataclass(frozen=True) # This is the immutable/hashable bit
 ... class A:
 ... i: int
 ...
 >>> @dataclass # This adds the mutable-but-comparable parts
 ... class B(A):
 ... j: int
 ... __hash__ = A.__hash__


However, disallowing this case for now *would* be a reasonable way to
postpone making a decision until 3.8 - in the meantime, folks would
still be able to experiment by overriding __setattr__ and __delattr__
after the dataclass decorator sets them.



For today, I'm going to make it an error to derive a frozen dataclass 
from a non-frozen dataclass, and also an error to derive a non-frozen 
dataclass from a frozen dataclass. With any luck (and Guido and Ned 
willing), we can address this by a3.


Eric

___
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] The `for y in [x]` idiom in comprehensions

2018-02-26 Thread Rob Cliffe via Python-Dev



On 22/02/2018 19:04, Serhiy Storchaka wrote:
Yet one discussion about reusing common subexpressions in 
comprehensions took place last week on the Python-ideas maillist (see 
topic "Temporary variables in comprehensions" [1]). The problem is 
that in comprehension like `[f(x) + g(f(x)) for x in range(10)]` the 
subexpression `f(x)` is evaluated twice. In normal loop you can 
introduce a temporary variable for `f(x)`. The OP wanted to add a 
special syntax for introducing temporary variables in comprehensions. 
This idea already was discussed multiple times in the past.


There are several ways of resolving this problem with existing syntax.

[snip]


Stephan Houben proposed an idiom which looks similar to new hypothetic 
syntax:


    result = [y + g(y) for x in range(10) for y in [f(x)]]

`for y in [expr]` in a comprehension means just assigning expr to y. I 
never seen this idiom before, but it can be a good replacement for a 
hypothetic syntax for assignment in comprehensions. It changes the 
original comprehension less than other approaches, just adds yet one 
element in a sequence of for-s and if-s. I think that after using it 
more widely it will become pretty idiomatic.


I have created a patch that optimizes this idiom, making it as fast as 
a normal assignment. [2] Yury suggested to ask Guido on the mailing 
list if he agrees that this language patten is worth 
optimizing/promoting.



Here's a thought: allow the syntax
    for VAR = EXPR
to define a for-loop that is executed exactly once (both inside and 
outside comprehensions), i.e. pretty much a synonym for

    for VAR in [ EXPR ]
    for VAR in ( EXPR , )
especially if Serhiy's optimisation means that the list/tuple is not 
actually constructed in the latter.


Pros:
(1) Stephan Houben's example could be written as
        result = [y + g(y) for x in range(10) for y = f(x)]
    which I find more readable.
(2) Code such as
            for i in xrange(10):
    could be changed on the fly to:
            for i = 1:
    I see this as especially useful in debugging, where you want to 
limit the program execution to a known problematic bit.

    But it some contexts it could be good style.
(3) Preserves the compatibility between a list comprehension and its 
"expansion" into for-loops.

(4) Backward compatible, since it is currently illegal syntax
(5) No extra keyword needed
(6) It goes some way towards providing the functionality of
            with VAR as EXPR
        that has been discussed multiple times.

Best wishes
Rob Cliffe
___
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] Should the dataclass frozen property apply to subclasses?

2018-02-26 Thread Ethan Furman

On 02/26/2018 05:40 AM, Eric V. Smith wrote:


As long as I'm saving if a dataclass is frozen, I should save all of the 
dataclass parameters on the class. Since it's
per-class, it's not a lot of overhead.


That's what Enum does.  A bit of pain, but makes so many other things easier.

--
~Ethan~

___
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] The `for y in [x]` idiom in comprehensions

2018-02-26 Thread Guido van Rossum
I would like to remind all wannabe language designers that grammar design
is not just solving puzzles. It's also about keeping the overall feel of
the language readable. I'm getting the idea that none of the proposals
discussed so far (whether new syntax or clever use of existing syntax)
satisfy that constraint. Sometimes a for-loop is just better.

-- 
--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] The `for y in [x]` idiom in comprehensions

2018-02-26 Thread Nick Coghlan
On 27 February 2018 at 05:08, Guido van Rossum  wrote:

> I would like to remind all wannabe language designers that grammar design
> is not just solving puzzles. It's also about keeping the overall feel of
> the language readable. I'm getting the idea that none of the proposals
> discussed so far (whether new syntax or clever use of existing syntax)
> satisfy that constraint. Sometimes a for-loop is just better.
>

+1

This is the main reason PEP 3150 (which adds a more limited form of
statement local named subexpressions) has spent more time Deferred than it
has ever being discussed as an active draft proposal: while naming
subexpressions is an occasionally attractive prospect, it's also an
addition that has significant potential to change the way various kinds of
code is typically written (even more so than something like type hints or
f-strings).

When even a PEP's author is thinking "I'm not sure this will actually be a
net improvement to the language", it's really not a good sign :)

Cheers,
Nick.

P.S. The comprehension-centric variants at least have the virtue of
precedent in Haskell's "let" clauses:
https://stackoverflow.com/questions/6067839/haskell-let-where-equivalent-within-list-comprehension/6067878#6067878

-- 
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] The `for y in [x]` idiom in comprehensions

2018-02-26 Thread Rob Cliffe via Python-Dev



On 26/02/2018 19:08, Guido van Rossum wrote:
I would like to remind all wannabe language designers that grammar 
design is not just solving puzzles. It's also about keeping the 
overall feel of the language readable. I'm getting the idea that none 
of the proposals discussed so far (whether new syntax or clever use of 
existing syntax) satisfy that constraint. Sometimes a for-loop is just 
better.


I don't know if you intended these remarks to include my proposal (to 
allow "for VAR = EXPR"), as your message was posted only 27 minutes 
after mine.
With respect, I honestly feel that this is a relatively small change 
that makes the language *more* readable.


Feel free, one and all, to tell me why I'm wrong.
Best wishes,
Rob Cliffe
___
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] The `for y in [x]` idiom in comprehensions

2018-02-26 Thread Guido van Rossum
On Mon, Feb 26, 2018 at 4:30 PM, Rob Cliffe via Python-Dev <
python-dev@python.org> wrote:


> On 26/02/2018 19:08, Guido van Rossum wrote:
>
> I would like to remind all wannabe language designers that grammar design
> is not just solving puzzles. It's also about keeping the overall feel of
> the language readable. I'm getting the idea that none of the proposals
> discussed so far (whether new syntax or clever use of existing syntax)
> satisfy that constraint. Sometimes a for-loop is just better.
>
> I don't know if you intended these remarks to include my proposal (to
> allow "for VAR = EXPR"), as your message was posted only 27 minutes after
> mine.
> With respect, I honestly feel that this is a relatively small change that
> makes the language *more* readable.
>
> Feel free, one and all, to tell me why I'm wrong.
> Best wishes,
> Rob Cliffe
>

I didn't want to single you out, but yes, I did include your proposal.

The reason is that for people who are not Python experts there's no obvious
reason why `for VAR = EXPR` should mean one thing and `for VAR in EXPR`
should mean another.

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