Re: Ensure unwanted names removed in class definition
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
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
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
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
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
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
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
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
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
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