Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #25166 > unrolled thread

lambda in list comprehension acting funny

Started byDaniel Fetchinson <fetchinson@googlemail.com>
First post2012-07-11 08:41 +0200
Last post2012-07-13 13:47 -0400
Articles 20 on this page of 48 — 18 participants

Back to article view | Back to comp.lang.python


Contents

  lambda in list comprehension acting funny Daniel Fetchinson <fetchinson@googlemail.com> - 2012-07-11 08:41 +0200
    Re: lambda in list comprehension acting funny "Colin J. Williams" <cjw@ncf.ca> - 2012-07-11 06:28 -0400
      Re: lambda in list comprehension acting funny Ian Kelly <ian.g.kelly@gmail.com> - 2012-07-11 10:34 -0600
        Re: lambda in list comprehension acting funny 88888 Dihedral <dihedral88888@googlemail.com> - 2012-07-11 20:39 -0700
          Re: lambda in list comprehension acting funny Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-07-12 03:51 +0000
            Re: lambda in list comprehension acting funny 88888 Dihedral <dihedral88888@googlemail.com> - 2012-07-11 22:04 -0700
              Re: lambda in list comprehension acting funny Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-07-12 06:18 +0000
                Re: lambda in list comprehension acting funny Mark Lawrence <breamoreboy@yahoo.co.uk> - 2012-07-12 08:40 +0100
        Re: lambda in list comprehension acting funny 88888 Dihedral <dihedral88888@googlemail.com> - 2012-07-11 20:39 -0700
    Re: lambda in list comprehension acting funny woooee <woooee@gmail.com> - 2012-07-11 11:38 -0700
      Re: lambda in list comprehension acting funny John Ladasky <john_ladasky@sbcglobal.net> - 2012-07-11 13:21 -0700
        Re: lambda in list comprehension acting funny Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-07-12 00:52 +0000
        Re: lambda in list comprehension acting funny Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2012-07-11 21:05 -0400
          Re: lambda in list comprehension acting funny Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-07-12 03:53 +0000
            Re: lambda in list comprehension acting funny Terry Reedy <tjreedy@udel.edu> - 2012-07-12 00:24 -0400
            Re: lambda in list comprehension acting funny Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2012-07-12 00:39 -0400
              Re: lambda in list comprehension acting funny Robert Miles <robertmiles@teranews.com> - 2012-08-15 19:26 -0500
        Re: lambda in list comprehension acting funny John O'Hagan <research@johnohagan.com> - 2012-07-12 15:29 +1000
        Re: lambda in list comprehension acting funny Robert Kern <robert.kern@gmail.com> - 2012-07-12 10:06 +0100
      Re: lambda in list comprehension acting funny Hans Mulder <hansmu@xs4all.nl> - 2012-07-12 00:22 +0200
      Re: lambda in list comprehension acting funny Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-07-11 23:47 +0000
        Re: lambda in list comprehension acting funny Daniel Fetchinson <fetchinson@googlemail.com> - 2012-07-12 04:54 +0200
    Re: lambda in list comprehension acting funny Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-07-12 03:59 +0000
      Re: lambda in list comprehension acting funny Ian Kelly <ian.g.kelly@gmail.com> - 2012-07-12 10:53 -0600
      Re: lambda in list comprehension acting funny Rotwang <sg552@hotmail.co.uk> - 2012-07-12 18:23 +0100
        Re: lambda in list comprehension acting funny Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-07-13 02:20 +0000
    Re: lambda in list comprehension acting funny rusi <rustompmody@gmail.com> - 2012-07-12 21:33 -0700
      Re: lambda in list comprehension acting funny Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-07-13 06:36 +0000
        Re: lambda in list comprehension acting funny rusi <rustompmody@gmail.com> - 2012-07-13 06:44 -0700
          Re: lambda in list comprehension acting funny rusi <rustompmody@gmail.com> - 2012-07-13 07:45 -0700
          RE: lambda in list comprehension acting funny "Prasad, Ramit" <ramit.prasad@jpmorgan.com> - 2012-07-13 16:12 +0000
            Re: lambda in list comprehension acting funny rusi <rustompmody@gmail.com> - 2012-07-13 09:46 -0700
              Re: lambda in list comprehension acting funny Chris Angelico <rosuav@gmail.com> - 2012-07-14 03:20 +1000
            Re: lambda in list comprehension acting funny Hans Mulder <hansmu@xs4all.nl> - 2012-07-13 19:53 +0200
              RE: lambda in list comprehension acting funny "Prasad, Ramit" <ramit.prasad@jpmorgan.com> - 2012-07-13 18:06 +0000
              Re: lambda in list comprehension acting funny Ian Kelly <ian.g.kelly@gmail.com> - 2012-07-13 12:54 -0600
                Re: lambda in list comprehension acting funny Hans Mulder <hansmu@xs4all.nl> - 2012-07-13 21:26 +0200
                Re: lambda in list comprehension acting funny Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-07-14 23:29 +0000
                  Re: lambda in list comprehension acting funny Chris Angelico <rosuav@gmail.com> - 2012-07-15 10:49 +1000
                    Re: lambda in list comprehension acting funny Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-07-15 08:32 +0000
                      Re: lambda in list comprehension acting funny Chris Angelico <rosuav@gmail.com> - 2012-07-15 18:44 +1000
                        Re: lambda in list comprehension acting funny Hans Mulder <hansmu@xs4all.nl> - 2012-07-15 21:25 +0200
                      Re: lambda in list comprehension acting funny Terry Reedy <tjreedy@udel.edu> - 2012-07-15 06:27 -0400
              Re: lambda in list comprehension acting funny rusi <rustompmody@gmail.com> - 2012-07-13 19:31 -0700
                Re: lambda in list comprehension acting funny Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-07-14 03:43 +0000
                  Re: lambda in list comprehension acting funny rusi <rustompmody@gmail.com> - 2012-07-13 21:53 -0700
                    Re: lambda in list comprehension acting funny Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-07-14 07:46 +0000
          Re: lambda in list comprehension acting funny Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2012-07-13 13:47 -0400

Page 2 of 3 — ← Prev page 1 [2] 3  Next page →


#25183

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-07-11 23:47 +0000
Message-ID<4ffe1096$0$29965$c3e8da3$5496439d@news.astraweb.com>
In reply to#25178
On Wed, 11 Jul 2012 11:38:18 -0700, woooee wrote:

> You should not be using lambda in this case 
> .for x in [2, 3]:
> .    funcs = [x**ctr for ctr in range( 5 )] 
> .    for p in range(5):
> .        print x, funcs[p]
> .    print

If you change the requirements, it's always easy to solve problems. But 
it is the wrong problem that you have solved.

The problem we have been asked to solve is to create a sequence of 
function objects, so that they can be called later, when needed, *not* to 
pre-calculate the results.

In this case, the most obvious solution is to store a local variable in 
each function object with the value you want.

funcs = [(lambda x, i=i: x**i) for i in range(5)]

creates a list of five functions:

    lambda x, i=0: x**i
    lambda x, i=1: x**i
    lambda x, i=2: x**i
    and so on.

In this case, each function has two local variables, x and i, with i 
having a default value. The function parameter i is bound to the value of 
i when the function was created.

Because parameter defaults are calculated once, when the function is 
created, this causes the value of i to stick to the newly created 
function, and we get the result we need.

What happens if you don't use a parameter with a default value?

funcs = [(lambda x: x**i) for i in range(5)]

In this case, each function body contains one local variable, x, and one 
non-local or global variable, i.

Because i is a non-local, the function doesn't store a value for it. 
Instead, the function stores a lump of data pointing to just enough of 
the environment to fetch the current value of the non-local i when 
needed. Since all five functions are in the same environment, they all 
see the same value of i when you call them, regardless of what the value 
of i was when they were created.

This is little different from doing this:

i = 1
def f1(x): return x**i

i = 2
def f2(x): return x**i

i = 3
def f3(x): return x**i

Is there any surprise that all three functions return the same value? 
They all point to the same global variable i. I'm not sure what it is 
about lambda that fools people into thinking that it is different (I've 
even been fooled myself!) but it is not.


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#25189

FromDaniel Fetchinson <fetchinson@googlemail.com>
Date2012-07-12 04:54 +0200
Message-ID<mailman.2021.1342061675.4697.python-list@python.org>
In reply to#25183
>> You should not be using lambda in this case
>> .for x in [2, 3]:
>> .    funcs = [x**ctr for ctr in range( 5 )]
>> .    for p in range(5):
>> .        print x, funcs[p]
>> .    print
>
> If you change the requirements, it's always easy to solve problems. But
> it is the wrong problem that you have solved.
>
> The problem we have been asked to solve is to create a sequence of
> function objects, so that they can be called later, when needed, *not* to
> pre-calculate the results.
>
> In this case, the most obvious solution is to store a local variable in
> each function object with the value you want.
>
> funcs = [(lambda x, i=i: x**i) for i in range(5)]
>
> creates a list of five functions:
>
>     lambda x, i=0: x**i
>     lambda x, i=1: x**i
>     lambda x, i=2: x**i
>     and so on.
>
> In this case, each function has two local variables, x and i, with i
> having a default value. The function parameter i is bound to the value of
> i when the function was created.
>
> Because parameter defaults are calculated once, when the function is
> created, this causes the value of i to stick to the newly created
> function, and we get the result we need.
>
> What happens if you don't use a parameter with a default value?
>
> funcs = [(lambda x: x**i) for i in range(5)]
>
> In this case, each function body contains one local variable, x, and one
> non-local or global variable, i.
>
> Because i is a non-local, the function doesn't store a value for it.
> Instead, the function stores a lump of data pointing to just enough of
> the environment to fetch the current value of the non-local i when
> needed. Since all five functions are in the same environment, they all
> see the same value of i when you call them, regardless of what the value
> of i was when they were created.
>
> This is little different from doing this:
>
> i = 1
> def f1(x): return x**i
>
> i = 2
> def f2(x): return x**i
>
> i = 3
> def f3(x): return x**i
>
> Is there any surprise that all three functions return the same value?
> They all point to the same global variable i. I'm not sure what it is
> about lambda that fools people into thinking that it is different (I've
> even been fooled myself!) but it is not.

Thank you Steve!
Precise and clear, as always!

Cheers,
Daniel

-- 
Psss, psss, put it down! - http://www.cafepress.com/putitdown

[toc] | [prev] | [next] | [standalone]


#25195

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-07-12 03:59 +0000
Message-ID<4ffe4bbb$0$1781$c3e8da3$76491128@news.astraweb.com>
In reply to#25166
On Wed, 11 Jul 2012 08:41:57 +0200, Daniel Fetchinson wrote:

> funcs = [ lambda x: x**i for i in range( 5 ) ]

Here's another solution:

from functools import partial
funcs = [partial(lambda i, x: x**i, i) for i in range(5)]


Notice that the arguments i and x are defined in the opposite order. 
That's because partial only applies positional arguments from the left. 
If there was a "right partial" that applies from the right, we could use 
the built-in instead:

funcs = [rpartial(pow, i) for i in range(5)]



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#25225

FromIan Kelly <ian.g.kelly@gmail.com>
Date2012-07-12 10:53 -0600
Message-ID<mailman.2048.1342112044.4697.python-list@python.org>
In reply to#25195
On Wed, Jul 11, 2012 at 9:59 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> On Wed, 11 Jul 2012 08:41:57 +0200, Daniel Fetchinson wrote:
>
>> funcs = [ lambda x: x**i for i in range( 5 ) ]
>
> Here's another solution:
>
> from functools import partial
> funcs = [partial(lambda i, x: x**i, i) for i in range(5)]
>
>
> Notice that the arguments i and x are defined in the opposite order.
> That's because partial only applies positional arguments from the left.
> If there was a "right partial" that applies from the right, we could use
> the built-in instead:
>
> funcs = [rpartial(pow, i) for i in range(5)]

You know, partial does handle keyword arguments correctly:

funcs = [partial(lambda x, i: x ** i, i=i) for i in range(5)]

This might be bad practice though if there are other arguments to the
right of the keywords that the eventual caller might want to specify,
as those arguments would then effectively be keyword-only.  The error
message in that case is not particularly illuminating, either:

>>> def f(a, b, c):
...   return (a, b, c)
...
>>> g = partial(f, b=42)
>>> g(3, 14)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() got multiple values for keyword argument 'b'

It would be a bit clearer if it instead noted that 'c' were
keyword-only in the 'g' argspec.

Cheers,
Ian

[toc] | [prev] | [next] | [standalone]


#25226

FromRotwang <sg552@hotmail.co.uk>
Date2012-07-12 18:23 +0100
Message-ID<jtn163$nlk$1@dont-email.me>
In reply to#25195
On 12/07/2012 04:59, Steven D'Aprano wrote:
> On Wed, 11 Jul 2012 08:41:57 +0200, Daniel Fetchinson wrote:
>
>> funcs = [ lambda x: x**i for i in range( 5 ) ]
>
> Here's another solution:
>
> from functools import partial
> funcs = [partial(lambda i, x: x**i, i) for i in range(5)]
>
>
> Notice that the arguments i and x are defined in the opposite order.
> That's because partial only applies positional arguments from the left.
> If there was a "right partial" that applies from the right, we could use
> the built-in instead:
>
> funcs = [rpartial(pow, i) for i in range(5)]

I don't think anyone has suggested this yet:

funcs = [(lambda i: lambda x: x**i)(i) for i in range(5)]

It's a bit more opaque than other solutions, but it fits in one line and 
avoids defining functions with an implicit second argument, if that's 
desirable for some reason.


-- 
I have made a thing that superficially resembles music:

http://soundcloud.com/eroneity/we-berated-our-own-crapiness

[toc] | [prev] | [next] | [standalone]


#25239

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-07-13 02:20 +0000
Message-ID<4fff85d5$0$29965$c3e8da3$5496439d@news.astraweb.com>
In reply to#25226
On Thu, 12 Jul 2012 18:23:10 +0100, Rotwang wrote:

> I don't think anyone has suggested this yet:
> 
> funcs = [(lambda i: lambda x: x**i)(i) for i in range(5)]

Oooh, nice! A factory function solution. Excellent!



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#25246

Fromrusi <rustompmody@gmail.com>
Date2012-07-12 21:33 -0700
Message-ID<03943ea6-5914-4de4-821c-055bed1d5804@f9g2000pbd.googlegroups.com>
In reply to#25166
On Jul 11, 11:41 am, Daniel Fetchinson <fetchin...@googlemail.com>
wrote:
> funcs = [ lambda x: x**i for i in range( 5 ) ]
> print funcs[0]( 2 )
> print funcs[1]( 2 )
> print funcs[2]( 2 )
>
> This gives me
>
> 16
> 16
> 16
>
> When I was excepting
>
> 1
> 2
> 4
>
> Does anyone know why?
>
> Cheers,
> Daniel

Your expectations are reasonable.  Heres the equivalent in Haskell
from which python has taken comprehensions.
---------------------------------
GHCi, version 7.4.1: http://www.haskell.org/ghc/  :? for help

Prelude> let funcs = [ (\ x -> x ^ i)| i <- [0..4]]
Prelude> (funcs !! 0)(2)
1
Prelude> (funcs !! 1)(2)
2
Prelude> (funcs !! 2)(2)
4
Prelude>

[toc] | [prev] | [next] | [standalone]


#25248

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-07-13 06:36 +0000
Message-ID<4fffc1fd$0$29965$c3e8da3$5496439d@news.astraweb.com>
In reply to#25246
On Thu, 12 Jul 2012 21:33:40 -0700, rusi wrote:

> On Jul 11, 11:41 am, Daniel Fetchinson <fetchin...@googlemail.com>
> wrote:
>> funcs = [ lambda x: x**i for i in range( 5 ) ] print funcs[0]( 2 )
>> print funcs[1]( 2 )
>> print funcs[2]( 2 )
>>
>> This gives me
>>
>> 16
>> 16
>> 16
>>
>> When I was excepting
>>
>> 1
>> 2
>> 4
>>
>> Does anyone know why?
>>
>> Cheers,
>> Daniel
> 
> Your expectations are reasonable.

You forget to finish that sentence.

"Your expectations are reasonable, for languages that don't have 
variables which can actually vary."

*wink*

For purely functional languages like Haskell, the behaviour you show 
below makes sense. Since Haskell doesn't allow variables to change their 
value, once a closure sees i=1 (say), then it must *always* see i=1.

But that's not the case in Python, where the Haskell behaviour would be 
unfortunate. Imagine if you did this:

VERBOSE = True

def function(arg):
    if VERBOSE:
        print("calling function with arg %r" % arg)
    process(arg)

You would expect the function to honour changes to the variable VERBOSE, 
would you not? Using Python's scoping rules for closures, it does. Using 
Haskell's rules, it wouldn't.

> Heres the equivalent in Haskell from which python has taken
> comprehensions.
> ---------------------------------
> GHCi, version 7.4.1: http://www.haskell.org/ghc/  :? for help
> 
> Prelude> let funcs = [ (\ x -> x ^ i)| i <- [0..4]] 
> Prelude> (funcs !! 0)(2)
> 1
> Prelude> (funcs !! 1)(2)
> 2
> Prelude> (funcs !! 2)(2)
> 4
> Prelude>



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#25263

Fromrusi <rustompmody@gmail.com>
Date2012-07-13 06:44 -0700
Message-ID<dc19f10f-018f-4859-8882-bc47d4b91b73@oo8g2000pbc.googlegroups.com>
In reply to#25248
On Jul 13, 11:36 am, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> On Thu, 12 Jul 2012 21:33:40 -0700, rusi wrote:
> > On Jul 11, 11:41 am, Daniel Fetchinson <fetchin...@googlemail.com>
> > wrote:
> >> funcs = [ lambda x: x**i for i in range( 5 ) ] print funcs[0]( 2 )
> >> print funcs[1]( 2 )
> >> print funcs[2]( 2 )
>
> >> This gives me
>
> >> 16
> >> 16
> >> 16
>
> >> When I was excepting
>
> >> 1
> >> 2
> >> 4
>
> >> Does anyone know why?
>
> >> Cheers,
> >> Daniel
>
> > Your expectations are reasonable.
>
> You forget to finish that sentence.
>
> "Your expectations are reasonable, for languages that don't have
> variables which can actually vary."
>
> *wink*
>
> For purely functional languages like Haskell, the behaviour you show
> below makes sense. Since Haskell doesn't allow variables to change their
> value, once a closure sees i=1 (say), then it must *always* see i=1.
>
> But that's not the case in Python, where the Haskell behaviour would be
> unfortunate. Imagine if you did this:
>
> VERBOSE = True
>
> def function(arg):
>     if VERBOSE:
>         print("calling function with arg %r" % arg)
>     process(arg)
>
> You would expect the function to honour changes to the variable VERBOSE,
> would you not? Using Python's scoping rules for closures, it does. Using
> Haskell's rules, it wouldn't.

Heres a more appropriate analog

------------------------------------------
VERBOSE = True

def function(arg):
    if VERBOSE:
       print("calling function with arg %r" % arg)
    process(arg)

def caller():
    VERBOSE = False
    function(1)

---------------------------------------------
Python semantics: function sees VERBOSE False
Haskell semantics: function sees VERBOSE True

[toc] | [prev] | [next] | [standalone]


#25264

Fromrusi <rustompmody@gmail.com>
Date2012-07-13 07:45 -0700
Message-ID<a5238568-1278-4385-873e-a2a6376a7a54@s6g2000pbi.googlegroups.com>
In reply to#25263
To come back to the OPs question.

Variables can be assigned. Or they can be bound.
[C++ programmers will be familiar with the difference between
initialization and assignment]

List comprehensions are defined in terms of assignment to the local
variable rather than binding.
Hence the issue.

Below are two functions that simulate mapping (lambda x: x**i) where
the i's come from some given list

def binding_version(lst):
    if not lst: return []
    i = lst[0]
    return [(lambda x: x ** i)] + binding_version(lst[1:])

def assign_version(lst):
    ret = []
    for i in lst:
        ret.append(lambda x: x**i)
    return ret
----------------------

>>> fs= binding_version([0,1,2])
>>> fs[0](2)
1
>>> fs[1](2)
2
>>> fs[2](2)
4

>>> fs= assign_version([0,1,2])
>>> fs[0](2)
4
>>> fs[1](2)
4
>>> fs[2](2)
4

[toc] | [prev] | [next] | [standalone]


#25269

From"Prasad, Ramit" <ramit.prasad@jpmorgan.com>
Date2012-07-13 16:12 +0000
Message-ID<mailman.2087.1342195975.4697.python-list@python.org>
In reply to#25263
> VERBOSE = True
> 
> def function(arg):
>     if VERBOSE:
>        print("calling function with arg %r" % arg)
>     process(arg)
> 
> def caller():
>     VERBOSE = False
>     function(1)
> 
> ---------------------------------------------
> Python semantics: function sees VERBOSE False
> Haskell semantics: function sees VERBOSE True

>>> def caller():
...     VERBOSE = False
...     function(1)
>>> def function(arg):
...     if VERBOSE:
...        print("calling function with arg %r" % arg)
...     
>>> VERBOSE = True
>>> caller()
calling function with arg 1

I might be being OCD, but caller needs `global VERBOSE` for that to
work as you explain.

Ramit


Ramit Prasad | JPMorgan Chase Investment Bank | Currencies Technology
712 Main Street | Houston, TX 77002
work phone: 713 - 216 - 5423

--

This email is confidential and subject to important disclaimers and
conditions including on offers for the purchase or sale of
securities, accuracy and completeness of information, viruses,
confidentiality, legal privilege, and legal entity disclaimers,
available at http://www.jpmorgan.com/pages/disclosures/email.  

[toc] | [prev] | [next] | [standalone]


#25272

Fromrusi <rustompmody@gmail.com>
Date2012-07-13 09:46 -0700
Message-ID<4deb2389-e20a-4520-964d-7d996cf6b9b4@m2g2000pbv.googlegroups.com>
In reply to#25269
On Jul 13, 9:12 pm, "Prasad, Ramit" <ramit.pra...@jpmorgan.com> wrote:
> > VERBOSE = True
>
> > def function(arg):
> >     if VERBOSE:
> >        print("calling function with arg %r" % arg)
> >     process(arg)
>
> > def caller():
> >     VERBOSE = False
> >     function(1)
>
> > ---------------------------------------------
> > Python semantics: function sees VERBOSE False
> > Haskell semantics: function sees VERBOSE True
> >>> def caller():
>
> ...     VERBOSE = False
> ...     function(1)>>> def function(arg):
>
> ...     if VERBOSE:
> ...        print("calling function with arg %r" % arg)
> ...    >>> VERBOSE = True
> >>> caller()
>
> calling function with arg 1
>
> I might be being OCD, but caller needs `global VERBOSE` for that to
> work as you explain.

Ok let me restate: if python were to work that way (without the
global) we could say either
a Python chooses to have dynamic scoping of variables
or
b There is a bug in python's scoping rules

I would guess that most younger folks (who've not seen lisp and apl)
would choose b

We can say the same analogously in the context of ZF expressions when
the i leaks as it does in the OPs example.

Another tack on the same question: python 3 cleaned up the variable
leakage from inside ZFs to outside.  It missed cleaning up the leakage
from one step to next.

tl;dr version: Beware of mixing up functional and imperative
programming
Double-beware when you are a language designer

[toc] | [prev] | [next] | [standalone]


#25274

FromChris Angelico <rosuav@gmail.com>
Date2012-07-14 03:20 +1000
Message-ID<mailman.2089.1342200007.4697.python-list@python.org>
In reply to#25272
On Sat, Jul 14, 2012 at 2:46 AM, rusi <rustompmody@gmail.com> wrote:
> Ok let me restate: if python were to work that way (without the
> global) we could say either
> a Python chooses to have dynamic scoping of variables
> or
> b There is a bug in python's scoping rules

Or c, there's a declaration at the top:

from __future__ import variable_declarations

Like braces, it's a MASSIVE improvement to the language, and it's part
of a huge conspiracy that it isn't there by default. But all it takes
is a little future statement and you're on Python 4000!

</troll>

ChrisA
(I shouldn't post at half past three in the morning. I get stupid... er.)

[toc] | [prev] | [next] | [standalone]


#25279

FromHans Mulder <hansmu@xs4all.nl>
Date2012-07-13 19:53 +0200
Message-ID<5000608c$0$6930$e4fe514c@news2.news.xs4all.nl>
In reply to#25269
On 13/07/12 18:12:40, Prasad, Ramit wrote:
>> VERBOSE = True
>>
>> def function(arg):
>>     if VERBOSE:
>>        print("calling function with arg %r" % arg)
>>     process(arg)
>>
>> def caller():
>>     VERBOSE = False
>>     function(1)
>>
>> ---------------------------------------------
>> Python semantics: function sees VERBOSE False
>> Haskell semantics: function sees VERBOSE True

>>>> def caller():
> ...     VERBOSE = False
> ...     function(1)
>>>> def function(arg):
> ...     if VERBOSE:
> ...        print("calling function with arg %r" % arg)
> ...     
>>>> VERBOSE = True
>>>> caller()
> calling function with arg 1
> 
> I might be being OCD, but caller needs `global VERBOSE` for that to
> work as you explain.

That would be quite different from what Rusi is after.

If you add `global VERBOSE` to `caller`, then there is only one
variable named `VERBOSE` and what `function` does, depends on
the most recent assignment to that variable.

If you remove your `global VERBOSE`, then there are two
variables by that name, one global and one local to `caller`.
In that case, there is the question of which one `function`
will use.

The function `function` refers to a variable `VERBOSE` that
isn't local.  In some programming langauages, the interpreter
would then scan the call stack at run-time, looking for a scope
where that name is defined.  It would find the local one in
`caller`.  This is known as "dynamic binding".

Other interpreters use the `VERBOSE` that was in scope at
the point in the program text where `function` was defined.
In this case, that would be the global one.  This is called
"lexical binding".

Some programming languages allow you to indicate on a per-
variable basis whether you want dynamic or lexical binding.

Python is firmly in the lexical camp.  Dynamic binding is not
available in Python, and never will be.


Hope this helps,

-- HansM

[toc] | [prev] | [next] | [standalone]


#25281

From"Prasad, Ramit" <ramit.prasad@jpmorgan.com>
Date2012-07-13 18:06 +0000
Message-ID<mailman.2095.1342202805.4697.python-list@python.org>
In reply to#25279
> >> VERBOSE = True
> >>
> >> def function(arg):
> >>     if VERBOSE:
> >>        print("calling function with arg %r" % arg)
> >>     process(arg)
> >>
> >> def caller():
> >>     VERBOSE = False
> >>     function(1)
> >>
> >> ---------------------------------------------
> >> Python semantics: function sees VERBOSE False
> >> Haskell semantics: function sees VERBOSE True
> 
> >>>> def caller():
> > ...     VERBOSE = False
> > ...     function(1)
> >>>> def function(arg):
> > ...     if VERBOSE:
> > ...        print("calling function with arg %r" % arg)
> > ...
> >>>> VERBOSE = True
> >>>> caller()
> > calling function with arg 1
> >
> > I might be being OCD, but caller needs `global VERBOSE` for that to
> > work as you explain.
> 
> That would be quite different from what Rusi is after.
> 
> If you add `global VERBOSE` to `caller`, then there is only one
> variable named `VERBOSE` and what `function` does, depends on
> the most recent assignment to that variable.
> 
> If you remove your `global VERBOSE`, then there are two
> variables by that name, one global and one local to `caller`.
> In that case, there is the question of which one `function`
> will use.
>

But that is not what Rusi writes.
"Python semantics: function sees VERBOSE False" <- function
will not see False without the use of global. 

> The function `function` refers to a variable `VERBOSE` that
> isn't local.  In some programming langauages, the interpreter
> would then scan the call stack at run-time, looking for a scope
> where that name is defined.  It would find the local one in
> `caller`.  This is known as "dynamic binding".
> 
> Other interpreters use the `VERBOSE` that was in scope at
> the point in the program text where `function` was defined.
> In this case, that would be the global one.  This is called
> "lexical binding".
> 
> Some programming languages allow you to indicate on a per-
> variable basis whether you want dynamic or lexical binding.
> 
> Python is firmly in the lexical camp.  Dynamic binding is not
> available in Python, and never will be.

True and a good explanation, but not what I understood
Rusi to mean.

Ramit



This email is confidential and subject to important disclaimers and
conditions including on offers for the purchase or sale of
securities, accuracy and completeness of information, viruses,
confidentiality, legal privilege, and legal entity disclaimers,
available at http://www.jpmorgan.com/pages/disclosures/email.  

[toc] | [prev] | [next] | [standalone]


#25284

FromIan Kelly <ian.g.kelly@gmail.com>
Date2012-07-13 12:54 -0600
Message-ID<mailman.2097.1342205674.4697.python-list@python.org>
In reply to#25279
On Fri, Jul 13, 2012 at 11:53 AM, Hans Mulder <hansmu@xs4all.nl> wrote:
> The function `function` refers to a variable `VERBOSE` that
> isn't local.  In some programming langauages, the interpreter
> would then scan the call stack at run-time, looking for a scope
> where that name is defined.  It would find the local one in
> `caller`.  This is known as "dynamic binding".
>
> Other interpreters use the `VERBOSE` that was in scope at
> the point in the program text where `function` was defined.
> In this case, that would be the global one.  This is called
> "lexical binding".
>
> Some programming languages allow you to indicate on a per-
> variable basis whether you want dynamic or lexical binding.
>
> Python is firmly in the lexical camp.  Dynamic binding is not
> available in Python, and never will be.

I don't believe that dynamic vs. lexical binding is what rusi was
attempting to describe.  If he was, then Python and Haskell would be a
bad comparison since both are lexical.  Rather, I think what he was
trying to show was capture by reference vs. capture by value in the
context of closures.  Python uses capture by reference, and so the
upvalue is the value of that reference at the time the closure is
called.  Haskell uses capture by value, and the upvalue is the value
at the time of definition.

I've also seen the distinction described as "early" vs. "late" binding
on this list, but I'm not sure how precise that is -- I believe that
terminology more accurately describes whether method and attribute
names are looked up at compile-time or at run-time, late binding being
the feature that makes duck typing possible.

Cheers,
Ian

[toc] | [prev] | [next] | [standalone]


#25285

FromHans Mulder <hansmu@xs4all.nl>
Date2012-07-13 21:26 +0200
Message-ID<5000764c$0$6941$e4fe514c@news2.news.xs4all.nl>
In reply to#25284
On 13/07/12 20:54:02, Ian Kelly wrote:
> I've also seen the distinction described as "early" vs. "late" binding
> on this list, but I'm not sure how precise that is -- I believe that
> terminology more accurately describes whether method and attribute
> names are looked up at compile-time or at run-time, late binding being
> the feature that makes duck typing possible.

I think that these terms describe the problem at the start of
this thread: the OP was expecting early binding.  However, Python
does late binding, or "acts funny" as it says in the subject line.


-- HansM

[toc] | [prev] | [next] | [standalone]


#25322

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-07-14 23:29 +0000
Message-ID<500200be$0$29995$c3e8da3$5496439d@news.astraweb.com>
In reply to#25284
On Fri, 13 Jul 2012 12:54:02 -0600, Ian Kelly wrote:

> On Fri, Jul 13, 2012 at 11:53 AM, Hans Mulder <hansmu@xs4all.nl> wrote:
>> The function `function` refers to a variable `VERBOSE` that isn't
>> local.  In some programming langauages, the interpreter would then scan
>> the call stack at run-time, looking for a scope where that name is
>> defined.  It would find the local one in `caller`.  This is known as
>> "dynamic binding".
>>
>> Other interpreters use the `VERBOSE` that was in scope at the point in
>> the program text where `function` was defined. In this case, that would
>> be the global one.  This is called "lexical binding".
>>
>> Some programming languages allow you to indicate on a per- variable
>> basis whether you want dynamic or lexical binding.
>>
>> Python is firmly in the lexical camp.  Dynamic binding is not available
>> in Python, and never will be.

I don't remember whether it is Javascript or PHP that uses dynamic 
binding, but whichever it is, it is generally considered to be a bad 
idea, at least as the default or only behaviour.

Bash is another language with dynamic binding. Some very old versions of 
Lisp use dynamic binding, because it was the easiest to implement. Most 
modern languages use lexical (also known as static) binding, because it 
is more sensible.

Here is an illustration of the difference: suppose we have two modules, 
library.py and main.py:

# library.py
x = 23
def func(y):
    return x + y

# main.py
import library
x = 1000
print func(1)


If main.py prints 24 (and it does), then Python is using lexical scoping. 
But if it prints 1001 (which it doesn't), then it is using dynamic 
scoping. The obvious problem with dynamic binding is that the behaviour 
of a function may vary depending on where you call it.



> I don't believe that dynamic vs. lexical binding is what rusi was
> attempting to describe.  If he was, then Python and Haskell would be a
> bad comparison since both are lexical.  Rather, I think what he was
> trying to show was capture by reference vs. capture by value in the
> context of closures.  Python uses capture by reference, and so the
> upvalue is the value of that reference at the time the closure is
> called.  Haskell uses capture by value, and the upvalue is the value at
> the time of definition.

I don't think "by reference" versus "by value" are good terms to use 
here, since they risk conflating the issue with "call by reference" 
versus "call by value" semantics. I prefer "late" versus "early", as you 
suggest below.

 
> I've also seen the distinction described as "early" vs. "late" binding
> on this list, but I'm not sure how precise that is -- I believe that
> terminology more accurately describes whether method and attribute names
> are looked up at compile-time or at run-time, 

Not necessarily *compile* time, but the distinction is between when the 
function is defined (which may at compile time, or it may be at run time) 
versus when the function is called. I think that gets to the heart of the 
issue, not whether the capture copies a value or a reference. At some 
point, the capture must make use of the value: whether it does so via a 
direct C-style memory location copy, or an object pointer, or some other 
mechanism, is irrelevant. What matters is whether that value is grabbed 
at the time the closure is created, or when the closure is called.


> late binding being the
> feature that makes duck typing possible.

That is conflating two entirely distinct subjects. Python 1.5 had duck-
typing but no closures, so the one certainly does not depend on the other.

Duck-typing is more a philosophy than a language feature: the language 
must be typed, and the programmer must prefer to program to a protocol or 
a specification rather than to membership of a type.


-- 
Steven

[toc] | [prev] | [next] | [standalone]


#25326

FromChris Angelico <rosuav@gmail.com>
Date2012-07-15 10:49 +1000
Message-ID<mailman.2123.1342313391.4697.python-list@python.org>
In reply to#25322
On Sun, Jul 15, 2012 at 9:29 AM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> Not necessarily *compile* time, but the distinction is between when the
> function is defined (which may at compile time, or it may be at run time)
> versus when the function is called.

I'd treat the def/lambda statement as "compile time" and the ()
operator as "run time".

ChrisA

[toc] | [prev] | [next] | [standalone]


#25340

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-07-15 08:32 +0000
Message-ID<5002802f$0$29995$c3e8da3$5496439d@news.astraweb.com>
In reply to#25326
On Sun, 15 Jul 2012 10:49:48 +1000, Chris Angelico wrote:

> On Sun, Jul 15, 2012 at 9:29 AM, Steven D'Aprano
> <steve+comp.lang.python@pearwood.info> wrote:
>> Not necessarily *compile* time, but the distinction is between when the
>> function is defined (which may at compile time, or it may be at run
>> time) versus when the function is called.
> 
> I'd treat the def/lambda statement as "compile time" and the () operator
> as "run time".

But function definitions occur at run time, not compile time -- they are 
executable statements, not instructions to the compiler to define a 
function.

For example:

py> dis("def f(x): return x+1")  # Python 3.2
  1           0 LOAD_CONST               0 (<code object f at 0xb7b57de0,
                                            file "<dis>", line 1>)
              3 MAKE_FUNCTION            0
              6 STORE_NAME               0 (f)
              9 LOAD_CONST               1 (None)
             12 RETURN_VALUE

The code object is pre-compiled at compile time, but the function and 
name-binding (the "def") doesn't occur until runtime.

At compile time, Python parses the source code and turns it into byte-
code. Class and function definitions are executed at run time, the same 
as any other statement.

I'm not sure if this is a difference that makes a difference or not; I 
think it is, but don't know enough about how closures and scoping rules 
work in other languages to be sure that it does make a difference.


-- 
Steven

[toc] | [prev] | [next] | [standalone]


Page 2 of 3 — ← Prev page 1 [2] 3  Next page →

Back to top | Article view | comp.lang.python


csiph-web