Explanation of this Python language feature? [x for x in x for x in x] (to flatten a nested list)
Rustom Mody
rustompmody at gmail.com
Sat Mar 22 00:39:55 EDT 2014
On Saturday, March 22, 2014 8:11:27 AM UTC+5:30, Chris Angelico wrote:
> On Sat, Mar 22, 2014 at 1:06 PM, Rustom Mody wrote:
> > Two: A comprehension variable is not bound but reassigned across the
> > comprehension. This problem remains in python3 and causes weird behavior when
> > lambdas are put in a comprehension
> >>>> fl = [lambda y : x+y for x in [1,2,3]]
> >>>> [fl[i](2) for i in [0,1,2]]
> > [5, 5, 5]
> To clarify, what you're saying here is that x in the first
> comprehension's closures should be bound to separate values for x,
> yes?
Yes
> I'm not sure how that ought to be done.
Thats an implementation question -- see below.
> Having closures that can
> reference and modify each other's variables is important.
Yes
> def func_pair():
> x = 0
> def inc():
> nonlocal x; x+=1
> return x
> def dec():
> nonlocal x; x-=1
> return x
> return inc, dec
> fooup, foodn = func_pair()
> barup, bardn = func_pair()
> >>> fooup(), fooup(), fooup(), foodn()
> (1, 2, 3, 2)
> >>> barup(), barup(), bardn(), bardn()
> (1, 2, 1, 0)
> Those functions are fundamentally linked. Very useful with callbacks.
> A nice alternative to doing everything with bound methods.
Yes
> So if that's not going to be broken, how is this fundamentally different?
> def func_loop():
> for x in 1,2,3:
> yield (lambda: x)
Thats using a for-loop
A 'for' in a comprehension carries a different intention, the matching names
being merely coincidental.
This 'pun' causes cognitive dissonance in all these questions, including
my gaffe above
> one, two, three = func_loop()
> one(), one(), two(), two(), three(), three()
> This one does NOT work the way the names imply, and I can see that
> you'd like to fix it. But I can't pinpoint a significant difference
> between them. How do you distinguish?
Using closures for carrying state is a different question
As for comprehensions, the appropriate *intention* would be like this:
Given
fl = [lambda y : x+y for x in [1,2,3]]
It means:
def rec(l):
if not l: return []
else:
x,ll = l[0],l[1:]
return [lambda y: x + y] + rec(ll)
followed by
fl = rec([1,2,3])
Naturally a reasonable *implementation* would carry this *intention* more
efficiently with standard techniques like
1. Using list extend/append methods
2. Using lambda y, x=x: x+y
Inside an implementation this is fine
Forcing such tricks as kosher on a programmer is not (IMHO)
[But then I find Lisp and much of basic haskell natural and most of C++ not,
so my views are likely prejudiced :-)
More information about the Python-list
mailing list