Closures python,scheme,ruby

Discussion in 'Python' started by paolo veronelli, Jul 12, 2004.

  1. I've a vague idea of the differences,I don't know scheme anyway.

    I'd like to see an example to show what is missing in python about
    closures and
    possibly understand if ruby is better in this sense.

    Iuse ruby and python in parallel for my job just to learn them and their
    differences and python is shorter and cleaner ,but i feel it's missing
    something,
    in closures.Any hints?

    grazie Paolino
    --
    .....lotta dura per la verdura
    paolo veronelli, Jul 12, 2004
    #1
    1. Advertising

  2. paolo veronelli <> wrote in message news:<>...
    > I've a vague idea of the differences,I don't know scheme anyway.
    >
    > I'd like to see an example to show what is missing in python about
    > closures and
    > possibly understand if ruby is better in this sense.
    >
    > Iuse ruby and python in parallel for my job just to learn them and their
    > differences and python is shorter and cleaner ,but i feel it's missing
    > something,
    > in closures.Any hints?
    >
    > grazie Paolino


    Python is cleaner but not shorter ;) I don't know about closures in Ruby,
    the basic difference between Python and Scheme is that Python
    closures are read-only, whereas Scheme closures are read-write
    (Scheme has a set! primitive). Here is an example:

    (define (outer)
    (let ((x 1))
    (lambda () (set! x 2) x)))

    ((outer))
    2

    You cannot do that in Python, unless you use ugly tricks with mutable objects.


    Michele Simionato
    Michele Simionato, Jul 12, 2004
    #2
    1. Advertising

  3. paolo veronelli <> writes:

    > I've a vague idea of the differences,I don't know scheme anyway.
    >
    > I'd like to see an example to show what is missing in python about
    > closures


    A long, long time ago, in a Python far away, there were three
    namesapaces: local (to a function), global and builtin. When looking
    up a name, Python would first search the local namespace (if there
    were one, i.e. we're inside a function), if the name were not found,
    it would search the global namespace, and, if still not found, fall
    back on the builtin namespace.

    (Let's forget about builtins, for this discussion. (You can always
    explicitly access the builtins namespace via __builtins__.))


    a = 3
    def fn():
    print a

    fn() # => 3

    No surprises there. "a" is found in the global namespace.

    a = 3
    def fn():
    a = 4
    print a # => 4

    fn()
    print a # => 3

    Inside "fn" a local variable "a" is created (shadowing the global
    one), it is printed, goes out of scope and disappears. Inside "fn" it
    shadowed the global "a" ... which is why the global "a" is unaffected
    by the "a = 4" inside "fn", and "3" was printed on the last line.

    Now, let's make a tiny alteration to the last example:

    a = 3
    def fn():
    print a # This is the added line
    a = 4
    print a

    fn()
    print a

    Run this, and you'll get an error, on the line we added:

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "/usr/tmp/python-BxFkNE", line 7, in ?
    fn()
    File "/usr/tmp/python-BxFkNE", line 3, in fn
    print a
    UnboundLocalError: local variable 'a' referenced before assignment

    Why?

    Because the compiler decides that "a" refers to a local variable in
    "fn" by noticing that there is an "a = ..." in the function body, and
    therefore never looks for "a" outside the local scope. But the first
    "print a" comes before that local variable is bound, so it's referring
    to a variable which has not been bound yet.

    This illustrates that the appearance of "a =" in a function body makes
    python believe that "a" is local. But what if you want the function to
    modify some global state? What if you want to modify the global "a" ?

    For this reason, Python has the "global" keyword:

    a = 3
    def fn():
    global a
    print a # => 3
    a = 4
    print a # => 4

    fn()
    print a # => 4

    All is perfect.

    But then, one dark day, nested scopes were added to Python. What does
    this mean? Well, in the good old times (say, python 1.5.2) you would
    get the following behaviour:

    a = 3
    def outer():
    a = 4
    def inner():
    print a
    return inner

    outer()() # => 3

    Why? "a" is not found in the local scope of "inner", so Python looks
    for it in the global scope, where it does find it ... and where it is
    bound to "3".

    But in a modern Python, the output of "outer()()" is "4", because
    nowadays Python looks for the name in any enclosing function-local
    scopes, before getting out to the global scope. So, in a modern
    Python, the "a = 4" binding, which is local to "outer" is found.

    Now, introducing a local binding of "a" in "inner", should offer no
    surprises:

    a = 3
    def outer():
    a = 4
    def inner():
    a = 5
    print a
    return inner

    outer()() # =>


    And adding the same "print a" we added to "fn" earlier ...


    a = 3
    def outer():
    a = 4
    def inner():
    print a
    a = 5
    print a
    return inner

    outer()() # =>

    .... gives us exactly the same error

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "/usr/tmp/python-BxFY2c", line 12, in ?
    outer()()
    File "/usr/tmp/python-BxFY2c", line 7, in inner
    print a
    UnboundLocalError: local variable 'a' referenced before assignment

    We could try to fix it, as we did before, by adding a "global a"
    declaration:

    a = 3
    def outer():
    a = 4
    def inner():
    global a
    print a # => 3
    a = 5
    print a # => 5
    return inner

    outer()() # =>


    .... but this results in the intermediate local function scope (the one
    of "outer") being ignored (as evidenced by the fact that the first
    "print a" gives us "3" rather than "4"). So, it seems that nested
    scopes were introduced into Python in a read-only fashion.

    The problem is that there is no way of saying "mutate the binding for
    the name, wherever you find it". You can say:

    a) Mutate or create a binding in the builtin scope:

    __builtins__.a = ...

    b) Mutate or create a binding in the global scope:

    global a
    a = ...

    c) Mutate or create a binding in the local scope:

    a = ...

    So, one solution might be to introduce a way of saying "Mutate the
    binding, wherever you find it":

    lexical a
    a =

    But, viewed from another perspective, the problem is that Python
    offers no way to distinguish between mutating existing bindings and
    creating new ones; both are spelt "=". If there were a way to specify
    whether you mean "create a new binding" or "rebind the name", then
    there would be no need for the "lexical" (or even the "global")
    declaration.

    This is what is done in some other languages. For example in Scheme:

    - Find the nearest binding of "a" and rebind it to "3":

    (set! a 3)

    - Create a new (lexically nested) variable "a" and bind it to "3":

    (let ((a 3) ...)


    So, what is missing about Python closures, is the mutability of the
    enclosed variables. There is a simple way of hacking around it: make
    the enclosed variable a mutable, and mutate it, rather than trying to
    rebind the name (thereby avoiding the "a =" syntax which tells Python
    to create a local binding of "a") ...

    def outer():
    a = [4]
    def inner():
    print a[0]
    a[0] = a[0] + 1
    return inner

    fn = outer()
    fn() # => 4
    fn() # => 5
    fn() # => 6

    It works, but it severely reduces the elegance and convenience of
    closures. If you appreciate closures, you are likely to consider this
    a big pain. If you don't appreciate closures you probabl can't see
    what the fuss is about, and are likely to tell people that stateful
    functions in Python should be implemented as methods of classes.

    I happen to appreciate closures.
    Jacek Generowicz, Jul 15, 2004
    #3
  4. Jacek Generowicz wrote:
    > [extensive description of the situation with Python closures]


    That's a great explanation, Jacek. Very informative.

    > It works, but it severely reduces the elegance and convenience
    > of closures. If you appreciate closures, you are likely to consider
    > this a big pain. If you don't appreciate closures you probably
    > can't see what the fuss is about, and are likely to tell people that
    > stateful functions in Python should be implemented as methods
    > of classes.
    >
    > I happen to appreciate closures.


    I really like closures too. They can make for some very simple and elegant
    code.

    For anyone who wonders what the fuss is about, I posted some Acrobat
    JavaScript code a while back with two versions of a piece of code, one with
    closures and one with objects:

    http://groups.google.com/groups?selm=

    -Mike
    Michael Geary, Jul 15, 2004
    #4
  5. On 15 Jul 2004 13:09:44 +0200, Jacek Generowicz <> wrote:
    [...]
    >This illustrates that the appearance of "a =" in a function body makes
    >python believe that "a" is local. But what if you want the function to
    >modify some global state? What if you want to modify the global "a" ?
    >

    I was going to pick a nit and point to a corner case where an "a = ..."
    in the function body still allows^Hed a (also) to refer to a non-local, i.e.,


    >>> def fn():

    ... a = a # local a used to get bound to non-local a, IIRC
    ... print a
    ...
    >>> import dis
    >>> dis.dis(fn)

    2 0 LOAD_FAST 0 (a)
    3 STORE_FAST 0 (a)

    3 6 LOAD_FAST 0 (a)
    9 PRINT_ITEM
    10 PRINT_NEWLINE
    11 LOAD_CONST 0 (None)
    14 RETURN_VALUE

    but code generation seems to have changed, or I misremember.
    Maybe the change is to make it absolutely consistent with determining
    semantics by full function body lookahead (which incidentally also
    rubs me the wrong way for discovering yields to change a function
    to a generator). I prefer meaning to depend just on what's
    been read so far, top to bottom, left to right, unless there is some
    overriding reason not to do it that way (e.g. normal expression and
    assignment evaluation order).

    I'm still a big python fan though ;-)
    [...]
    >I happen to appreciate closures.

    I do too. What if
    x <= expr
    meant evaluate expr and then
    find x (as if to do a read access) and rebind it, whatever name space it's found in?
    (Might be dangerous if you include searching builtins though)
    Hm, ... not sure about rebinding a binding discovered by attribute name access -- i.e.,
    x.y <= expr
    might rebind a class variable somewhere or an instance variable, depending.
    Don't know how useful that might be.

    Regards,
    Bengt Richter
    Bengt Richter, Jul 16, 2004
    #5
  6. (Bengt Richter) writes:

    > What if
    > x <= expr
    > meant evaluate expr and then find x (as if to do a read access) and
    > rebind it, whatever name space it's found in?


    I suspect that it's too Perlish for Python, but that sort of approach
    is probably the simplest, clearest, most extensible ...

    [Aside:

    It's in situations like this that sexpr-based languages are very
    adavantageous: you don't have to invent fancy syntax, you don't have
    to come up with new keywords that break old code ... you just say what
    you mean in the usual fashion:

    (find-and-rebind x expr)

    [Of course, you can still argue endlessly over how it should be spelt
    (rebind, set!, setq, lexical-set ...) but at least the mechanism is
    clear and uncontroversial.]

    ]

    > (Might be dangerous if you include searching builtins though)


    Yes, you'd probably want to keep builtins out of it.

    > Hm, ... not sure about rebinding a binding discovered by attribute
    > name access -- i.e.,
    > x.y <= expr
    > might rebind a class variable somewhere or an instance variable, depending.
    > Don't know how useful that might be.


    Could be fun ! :)
    Jacek Generowicz, Jul 16, 2004
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Kasper B. Graversen

    Closures in python

    Kasper B. Graversen, Sep 18, 2003, in forum: Python
    Replies:
    11
    Views:
    698
    David Eppstein
    Sep 21, 2003
  2. Robert Brewer

    RE: Closures python,scheme,ruby

    Robert Brewer, Jul 16, 2004, in forum: Python
    Replies:
    2
    Views:
    288
    Sean Ross
    Jul 16, 2004
  3. Karl Kofnarson

    What are python closures realy like?

    Karl Kofnarson, Dec 1, 2006, in forum: Python
    Replies:
    16
    Views:
    471
    Roy Smith
    Dec 12, 2006
  4. treble54

    Closures / Blocks in Python

    treble54, Jul 24, 2007, in forum: Python
    Replies:
    10
    Views:
    796
    Gabriel Genellina
    Jul 26, 2007
  5. zuzu
    Replies:
    12
    Views:
    295
    Florian Gross
    Aug 27, 2004
Loading...

Share This Page