Re: Ensure unwanted names removed in class definition

2015-08-13 Thread Peter Otten
Ben Finney wrote:

> Peter Otten <__pete...@web.de> writes:
> 
>> Ben Finney wrote:
>>
>> > Peter Otten <__pete...@web.de> writes:
>> > 
>> > That's an unexpected inconsistency between list comprehensions
>> > versus generator expressions, then. Is that documented explicitly in
>> > the Python 2 documentation?
>>
>> https://docs.python.org/2.4/whatsnew/node4.html
> 
> Or
> https://docs.python.org/2/whatsnew/2.4.html#pep-289-generator-expressions>.
> 
> Also in the PEP that introduces generator expressions, PEP 289:
> 
> List comprehensions also "leak" their loop variable into the
> surrounding scope. This will also change in Python 3.0, so that the
> semantic definition of a list comprehension in Python 3.0 will be
> equivalent to list().
> 
> https://www.python.org/dev/peps/pep-0289/#the-details>
> 
> Thanks for seeking the answer. Can you describe an improvement to
> https://docs.python.org/2/reference/expressions.html#list-displays>
> that makes clear this unexpected, deprecated behaviour which is only in
> Python 2?
> 

A sentence like that in the PEP would help, but I think the main problem is 
that list comprehensions and lists are subsumed under list displays. 
>From a user perspective

[f(x) for x in y]

has more in common with {x: f(x) for x in y} than with [x, y, z].

I think it would be great if list comprehensions could be moved in a 
separate paragraph or into

5.2.5 Displays for [lists,] sets and dictionaries

with a note

For backwards compatibility list comprehensions leak their loop variables 
into the surrounding scope. Set and dictionary displays do not leak.

I don't know if such a modification is feasible; I'm definitely no "language 
lawyer". 

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ensure unwanted names removed in class definition

2015-08-12 Thread Ben Finney
Peter Otten <__pete...@web.de> writes:

> Ben Finney wrote:
>
> > Peter Otten <__pete...@web.de> writes:
> > 
> > That's an unexpected inconsistency between list comprehensions
> > versus generator expressions, then. Is that documented explicitly in
> > the Python 2 documentation?
>
> https://docs.python.org/2.4/whatsnew/node4.html

Or 
https://docs.python.org/2/whatsnew/2.4.html#pep-289-generator-expressions>.

Also in the PEP that introduces generator expressions, PEP 289:

List comprehensions also "leak" their loop variable into the
surrounding scope. This will also change in Python 3.0, so that the
semantic definition of a list comprehension in Python 3.0 will be
equivalent to list().

https://www.python.org/dev/peps/pep-0289/#the-details>

Thanks for seeking the answer. Can you describe an improvement to
https://docs.python.org/2/reference/expressions.html#list-displays>
that makes clear this unexpected, deprecated behaviour which is only in
Python 2?

-- 
 \ “What is needed is not the will to believe but the will to find |
  `\   out, which is the exact opposite.” —Bertrand Russell, _Free |
_o__)   Thought and Official Propaganda_, 1928 |
Ben Finney

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ensure unwanted names removed in class definition

2015-08-12 Thread Peter Otten
Ben Finney wrote:

> Peter Otten <__pete...@web.de> writes:
> 
>> I would probably use a generator expression. These don't leak names:
> 
> That's an unexpected inconsistency between list comprehensions versus
> generator expressions, then. Is that documented explicitly in the Python
> 2 documentation?

https://docs.python.org/2/reference/expressions.html

has one sentence

"""
Note that the comprehension is executed in a separate scope, so names 
assigned to in the target list don’t “leak” in the enclosing scope.
"""

-- which is wrong unless I'm misunderstanding something, but

https://docs.python.org/2.4/whatsnew/node4.html

clearly states

"""
Generator expressions differ from list comprehensions in various small ways. 
Most notably, the loop variable (obj in the above example) is not accessible 
outside of the generator expression. List comprehensions leave the variable 
assigned to its last value; future versions of Python will change this, 
making list comprehensions match generator expressions in this respect.
"""


-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ensure unwanted names removed in class definition

2015-08-12 Thread Ben Finney
MRAB  writes:

> Have you thought about catching the NameError?

I had not, but that is obvious now you say it. Thanks.

Where there isn't a more elegant solution, I'll use that. It might not
be elegant, but it's at least clear and expressive of the intent.


Jussi Piitulainen  writes:

> Make them an implementation detail:
>
> plumage = [
> (__foo__, __bar__) for (__foo__, __bar__) in feathers.items()
> if __bar__ == "beautiful" ]

That still makes the name persist, so doesn't meet the requirements. It
also commits the further mistake of using a ‘__dunder__’ name style for
something that *isn't* a Python magic attribute.

Thanks for the attempt :-)

-- 
 \ “Teeth extracted by the latest Methodists.” dentist |
  `\  advertisement, Hong Kong |
_o__)  |
Ben Finney

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ensure unwanted names removed in class definition

2015-08-12 Thread Ben Finney
Peter Otten <__pete...@web.de> writes:

> I would probably use a generator expression. These don't leak names:

That's an unexpected inconsistency between list comprehensions versus
generator expressions, then. Is that documented explicitly in the Python
2 documentation?

> Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
> [GCC 4.8.2] on linux2
> Type "help", "copyright", "credits" or "license" for more information.
> >>> class Parrot:
> ... a = [per for per in "abc"]
> ... b = list(trans for trans in "def")
> ... 
> >>> Parrot.per
> 'c'
> >>> Parrot.trans
> Traceback (most recent call last):
>   File "", line 1, in 
> AttributeError: class Parrot has no attribute 'trans'

We have a winner! That's elegant, expressive, and has the right
behaviour on both Python 2 and Python 3.

-- 
 \ “I turned to speak to God/About the world's despair; But to |
  `\   make bad matters worse/I found God wasn't there.” —Robert Frost |
_o__)  |
Ben Finney

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ensure unwanted names removed in class definition

2015-08-12 Thread Jussi Piitulainen
Ben Finney writes:

> How can I ensure incidental names don't end up in the class
> definition, with code that works on both Python 2 and Python 3?
>
> With the following class definition, the incidental names `foo` and
> `bar`, only needed for the list comprehension, remain in the `Parrot`
> namespace::
>
> __metaclass__ = object
>
> class Parrot:
> """ A parrot with beautiful plumage. """
>
> plumage = [
> (foo, bar) for (foo, bar) in feathers.items()
> if bar == "beautiful"]
>
> assert hasattr(Parrot, 'plumage')  # ← okay, has the wanted name
> assert not hasattr(Parrot, 'foo')  # ← FAILS, has an unwanted name
> assert not hasattr(Parrot, 'bar')  # ← FAILS, has an unwanted name
[- -]
> How can I write the class definition with the list comprehension and
> *not* keep the incidental names — in code that will run correctly on
> both Python 2 and Python 3?

Make them an implementation detail:

plumage = [
(__foo__, __bar__) for (__foo__, __bar__) in feathers.items()
if __bar__ == "beautiful" ]
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ensure unwanted names removed in class definition

2015-08-12 Thread Chris Angelico
On Thu, Aug 13, 2015 at 1:39 AM, Peter Otten <__pete...@web.de> wrote:
> But I would probably use a generator expression. These don't leak names:
>
> Python 2.7.6 (default, Jun 22 2015, 17:58:13)
> [GCC 4.8.2] on linux2
> Type "help", "copyright", "credits" or "license" for more information.
 class Parrot:
> ... a = [per for per in "abc"]
> ... b = list(trans for trans in "def")
> ...

Ooh neat trick! Much cleaner than the explicit lambda that I
suggested. Withdrawing my recommendation in favour of this (but with
an explanatory comment explaining why list(genexp) is used rather than
a list comp).

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ensure unwanted names removed in class definition

2015-08-12 Thread Peter Otten
Ben Finney wrote:

> How can I ensure incidental names don't end up in the class definition,
> with code that works on both Python 2 and Python 3?
> 
> With the following class definition, the incidental names `foo` and
> `bar`, only needed for the list comprehension, remain in the `Parrot`
> namespace::
> 
> __metaclass__ = object
> 
> class Parrot:
> """ A parrot with beautiful plumage. """
> 
> plumage = [
> (foo, bar) for (foo, bar) in feathers.items()
> if bar == "beautiful"]
> 
> assert hasattr(Parrot, 'plumage')  # ← okay, has the wanted name
> assert not hasattr(Parrot, 'foo')  # ← FAILS, has an unwanted name
> assert not hasattr(Parrot, 'bar')  # ← FAILS, has an unwanted name
> 
> So I can remove those names after using them::
> 
> __metaclass__ = object
> 
> class Parrot:
> """ A parrot with beautiful plumage. """
> 
> plumage = [
> (foo, bar) for (foo, bar) in feathers.items()
> if bar == "beautiful"]
> del foo, bar
> 
> assert hasattr(Parrot, 'plumage')  # ← okay, has the wanted name
> assert not hasattr(Parrot, 'foo')  # ← okay, no unwanted name
> assert not hasattr(Parrot, 'bar')  # ← okay, no unwanted name
> 
> But that fails on Python 3, since the names *don't* persist from the
> list comprehension:
> 
> __metaclass__ = object
> 
> class Parrot:
> """ A parrot with beautiful plumage. """
> 
> plumage = [
> (foo, bar) for (foo, bar) in feathers.items()
> if bar == "beautiful"]
> del foo, bar  # ← FAILS, “NameError: name 'foo' is not defined”
> 
> How can I write the class definition with the list comprehension and
> *not* keep the incidental names — in code that will run correctly on
> both Python 2 and Python 3?

If you absolutely must: make sure that the names exist by setting them 
explicitly:

class Parrot:
   per = None
   a = [... for per in ...]
   del per

But I would probably use a generator expression. These don't leak names:

Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class Parrot:
... a = [per for per in "abc"]
... b = list(trans for trans in "def")
... 
>>> Parrot.per
'c'
>>> Parrot.trans
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: class Parrot has no attribute 'trans'


-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Ensure unwanted names removed in class definition

2015-08-12 Thread MRAB

On 2015-08-12 10:01, Ben Finney wrote:

How can I ensure incidental names don't end up in the class definition,
with code that works on both Python 2 and Python 3?

With the following class definition, the incidental names `foo` and
`bar`, only needed for the list comprehension, remain in the `Parrot`
namespace::

 __metaclass__ = object

 class Parrot:
 """ A parrot with beautiful plumage. """

 plumage = [
 (foo, bar) for (foo, bar) in feathers.items()
 if bar == "beautiful"]

 assert hasattr(Parrot, 'plumage')  # ← okay, has the wanted name
 assert not hasattr(Parrot, 'foo')  # ← FAILS, has an unwanted name
 assert not hasattr(Parrot, 'bar')  # ← FAILS, has an unwanted name

So I can remove those names after using them::

 __metaclass__ = object

 class Parrot:
 """ A parrot with beautiful plumage. """

 plumage = [
 (foo, bar) for (foo, bar) in feathers.items()
 if bar == "beautiful"]
 del foo, bar

 assert hasattr(Parrot, 'plumage')  # ← okay, has the wanted name
 assert not hasattr(Parrot, 'foo')  # ← okay, no unwanted name
 assert not hasattr(Parrot, 'bar')  # ← okay, no unwanted name

But that fails on Python 3, since the names *don't* persist from the
list comprehension:

 __metaclass__ = object

 class Parrot:
 """ A parrot with beautiful plumage. """

 plumage = [
 (foo, bar) for (foo, bar) in feathers.items()
 if bar == "beautiful"]
 del foo, bar  # ← FAILS, “NameError: name 'foo' is not defined”

How can I write the class definition with the list comprehension and
*not* keep the incidental names — in code that will run correctly on
both Python 2 and Python 3?


Have you thought about catching the NameError?

--
https://mail.python.org/mailman/listinfo/python-list


Re: Ensure unwanted names removed in class definition

2015-08-12 Thread Chris Angelico
On Wed, Aug 12, 2015 at 7:01 PM, Ben Finney  wrote:
> class Parrot:
> """ A parrot with beautiful plumage. """
>
> plumage = [
> (foo, bar) for (foo, bar) in feathers.items()
> if bar == "beautiful"]
> del foo, bar  # ← FAILS, “NameError: name 'foo' is not defined”
>
> How can I write the class definition with the list comprehension and
> *not* keep the incidental names — in code that will run correctly on
> both Python 2 and Python 3?

You could always do explicitly what a Py3 comprehension does, and wrap
it in a function:

plumage = (lambda: [
(foo, bar) for (foo, bar) in feathers.items()
if bar == "beautiful"])()

Hardly clean code, but it will work, and apart from having a redundant
layer of protection in Py3, will do exactly the same thing on both. Is
the lambda nesting cruft worth it?

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list