3

In SML (a functional programming language that I learned before Python), I can do the following:

val x = 3;
fun f() = x;
f();
>>> 3
val x = 7;
f();
>>> 3

In Python, however, the first call will give 3 and the second one will give 7.

x = 3
def f(): return x
f()
>>> 3
x = 7
f()
>>> 7

How do I bind the value of a variable to a function in Python?

Lanaru
  • 9,421
  • 7
  • 38
  • 64
  • another question, why do you need this? – tuxtimo Aug 14 '12 at 13:31
  • 1
    @tuxtimo -- This is actually *very* important. I use this sort of thing all the time binding callbacks to GUI widgets inside of loops. – mgilson Aug 14 '12 at 13:33
  • @tuxtimo Pretty much the same reason mcgilson stated. I need such a thing to generate a list of functions that refer to objects that are being iterated through in a `for` loop. – Lanaru Aug 14 '12 at 13:35
  • The difference is that all variables in Python are mutable (SML `ref`s), so `x = 7` is more like SML's `x := 7` in ML than it is `val x = 7`. (The first `x = 3` is like `val x = ref 3`, I guess.) – Antal Spector-Zabusky Aug 14 '12 at 18:52

4 Answers4

7

You can use a keyword argument:

x = 3
def f( x=x ): 
    return x

x = 7
f()  # 3

Keyword arguments are assigned when the function is created. Other variables are looked up in the function's scope when the function is run. (If they're not found in the function's scope, python looks for the variable in the scope containing the function, etc, etc.).

mgilson
  • 300,191
  • 65
  • 633
  • 696
2

You could do:

   x = 3
   f = lambda y=x: y
   f()
   >>> 3
   x = 7
   f()
   >>> 3
Neal
  • 6,722
  • 4
  • 38
  • 31
2

As mgilson and Neal the most straightforward way of doing this is to use a default argument when you define f.

A bigger issue is the philosophical mismatch here. Python does closures, but they're going to operate differently than you expect from SML (which I'm not particularly familiar with) or Haskell or Clojure or something (both of which I'm more familiar with). This is because of the way Python handles closures, but also because of the way it defines anonymous functions differently and allows side-effects.

Typically, where functional approaches don't work well, in Python you use a class, in this case to capture the value of x.

class F(object):
    def __init__(self, x):
        self.x = x
    def __call__(self):
        return self.x
x = 3
f = F(x)
f()
# 3
x = 7
f()
# 3

This is way, way overkill for this example, and I obviously wouldn't recommend it here. But often in Python, the answer is to use a class. (That is a gross over-statement, and my brain's spawning exceptions faster than I can type them. But when you're coming from a functional background, I think that this statement is a good heuristic as you're learning to approach problems Pythonically.)

As an aside, it's also interesting that this makes explicit what capturing the variable in the function argument definition does implicitly: it creates a function object that hangs onto the value of x at the point the object is created.

Eric
  • 311
  • 1
  • 3
0

This question appears to be about how to capture the value of (not a reference to) an outer variable at the time that a closure is created. And in that sense the usual answers are to use default arguments:

def f(x=x): 
    return x
# or
f = lambda x=x: x

(however, this won't work if you want a function that takes variable number of arguments). Or more generally (works even in the variable arguments case) but less pretty, using an explicit wrapping function that gets executed immediately:

f = (lambda x: lambda: x)(x)

However, I just wanted to point out that the premise of your question is not valid -- in SML, you cannot assign to a variable, ever (I'm not talking about modifying a mutable data structure like ref; I'm talking about assigning to variables); there is not even syntax in the language to do this.

What you are doing in your SML code is creating a new scope and defining a different variable with the same name x in the new scope, which hides the previous x. You can always simply rename that second variable to a different name and have no effect on the program. Now, Python just has function scope, and has no inner scopes for blocks of code inside a function. But you can apply the same solution -- just rename that second variable to a different name, and you won't have this problem.

newacct
  • 119,665
  • 29
  • 163
  • 224