Binding an objects value within a function (closure)

烈酒焚心 提交于 2019-11-27 08:05:26

问题


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?


回答1:


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.).




回答2:


You could do:

   x = 3
   f = lambda y=x: y
   f()
   >>> 3
   x = 7
   f()
   >>> 3



回答3:


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.




回答4:


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.



来源:https://stackoverflow.com/questions/11953330/binding-an-objects-value-within-a-function-closure

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!