Path: csiph.com!x330-a1.tempe.blueboxinc.net!newsfeed.hal-mli.net!feeder1.hal-mli.net!feeder.news-service.com!news2.euro.net!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; '(except': 0.05; '3.2': 0.05; 'cpython': 0.07; 'raises': 0.07; 'terry': 0.07; '>>>>': 0.09; '[0,': 0.09; 'called.': 0.09; 'received:80.91': 0.09; 'received:80.91.229': 0.09; 'received:80.91.229.12': 0.09; 'received:gmane.org': 0.09; 'received:list': 0.09; 'received:lo.gmane.org': 0.09; 'output': 0.11; 'def': 0.12; 'am,': 0.14; 'wrote:': 0.14; 'defined': 0.14; '(lambda': 0.16; '*after*': 0.16; 'bytecode': 0.16; 'captured': 0.16; 'expression,': 0.16; 'lambda': 0.16; 'lookup': 0.16; 'naming': 0.16; 'reedy': 0.16; 'unbound': 0.16; 'dependent': 0.16; 'jan': 0.20; 'header:In-Reply- To:1': 0.21; 'loop,': 0.23; 'code': 0.24; 'values': 0.25; 'later': 0.26; 'skip:p 30': 0.28; 'import': 0.29; 'functions.': 0.29; 'instead': 0.29; 'equivalent': 0.31; 'header:X-Complaints-To:1': 0.32; 'does': 0.33; 'to:addr:python-list': 0.33; 'list': 0.33; 'header:User-Agent:1': 0.35; 'constant': 0.35; 'ways': 0.37; 'another': 0.37; 'received:org': 0.38; 'but': 0.38; 'subject:: ': 0.38; 'header:Mime-Version:1': 0.39; 'to:addr:python.org': 0.39; 'error.': 0.40; 'your': 0.60; '!!!': 0.65; 'here': 0.66; '(n)': 0.84; 'irrelevant': 0.84 X-Injected-Via-Gmane: http://gmane.org/ To: python-list@python.org From: Terry Reedy Subject: Re: Something is rotten in Denmark... Date: Tue, 31 May 2011 13:11:43 -0400 References: Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit X-Gmane-NNTP-Posting-Host: rain.gmane.org User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.17) Gecko/20110414 Lightning/1.0b2 Thunderbird/3.1.10 In-Reply-To: X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 107 NNTP-Posting-Host: 82.94.164.166 X-Trace: 1306861916 news.xs4all.nl 49179 [::ffff:82.94.164.166]:43142 X-Complaints-To: abuse@xs4all.nl Xref: x330-a1.tempe.blueboxinc.net comp.lang.python:6745 On 5/31/2011 2:48 AM, harrismh777 wrote: >>>> fs=[] Irrelevant here since you immediately rebind 'fs'. >>>> fs = [(lambda n: i + n) for i in range(10)] >>>> [fs[i](1) for i in range(10)] Same as [f(1) for f in fs] > [10, 10, 10, 10, 10, 10, 10, 10, 10, 10] <=== not good > > ( that was a big surprise! . . . ) You have been hypnotizeed by lambda. (lambda n: i+n) is a *constant expression*, so you get 10 'equal' functions. To see this better fs = [(lambda n: i + n) for i in range(10)] from dis import dis for f in fs: dis(f) 1 0 LOAD_DEREF 0 (i) 3 LOAD_FAST 0 (n) 6 BINARY_ADD 7 RETURN_VALUE 1 0 LOAD_DEREF 0 (i) 3 LOAD_FAST 0 (n) 6 BINARY_ADD 7 RETURN_VALUE ... All have the same bytecode and all retrieve the same last value of i in the nonlocal listcomp scope when you call them *after* the listcomp scope has otherwise disappeared. Your code is equivalent to fs = [] for i in range(10): fs.append(lambda n: i + n) print([f(1) for f in fs]) which is equivalent (except for naming the functions) to fs = [] for i in range(10): def f(n): return i + n fs.append(f) print([f(1) for f in fs]) Does [10, 10, 10, 10, 10, 10, 10, 10, 10, 10] still surprise? Because the def is a constant expression, we can move it out of the loop, and get the equivalent (except for identity) def f(n): return i + n fs = [] for i in range(10): fs.append(f) print([f(1) for f in fs]) This in turn is equivalent to def f(n): return i + n fs = [] for _ in range(10): fs.append(f) i=9 print([f(1) for f in fs]) which in turn is equivalent in output to def f(n): return i + n i = 9 print([f(1) for _ in range(10)]) Note that: def f(n): return i+n # or f = lambda n: i+n fs = [f for i in range(10)] print([f(1) for f in fs]) works in 2.7, with the same output, but not in 3.2 because in 3.x, i is local to the list comp and the later call raises unbound global error. > ( let's try it another way . . . ) All these other ways create 10 *different* (unequal) functions that are different because they have captured 10 different values of i when defined instead of deferring lookup of i to when they are called. def g(i): return (lambda n: i + n) fs = [g(i) for i in range(10)] print([f.__closure__[0].cell_contents for f in fs]) fs = [(lambda n, i=i: i + n) for i in range(10)] print([f.__defaults__[0] for f in fs]) # CPython 3.2 dependent code !!! [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] -- Terry Jan Reedy