Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/62.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在循环中创建函数_Python_Function - Fatal编程技术网

Python 在循环中创建函数

Python 在循环中创建函数,python,function,Python,Function,我试图在循环中创建函数: functions = [] for i in range(3): def f(): return i # alternatively: f = lambda: i functions.append(f) 问题是所有函数最终都是相同的。这三个函数都返回2,而不是返回0、1和2: print([f() for f in functions]) # expected output: [0, 1, 2] # actual out

我试图在循环中创建函数:

functions = []

for i in range(3):
    def f():
        return i

    # alternatively: f = lambda: i

    functions.append(f)
问题是所有函数最终都是相同的。这三个函数都返回2,而不是返回0、1和2:

print([f() for f in functions])
# expected output: [0, 1, 2]
# actual output:   [2, 2, 2]

为什么会发生这种情况,我应该怎么做才能得到分别输出0、1和2的3个不同函数?

您遇到了延迟绑定的问题——每个函数都尽可能晚地查找
I
(因此,当在循环结束后调用时,
I
将设置为
2

通过强制提前绑定可以轻松修复:将
def()更改为
def(i=i):
如下:

def f(i=i):
    return i
global_var = 'foo'

def my_function():
    print(global_var)

global_var = 'bar'
my_function()
默认值(在
i=i
中右边的
i
是参数名
i
的默认值,而在
i=i
中左边的
i
是在
def
时间查找的,而不是在
调用
时间查找的,因此本质上它们是专门查找早期绑定的一种方法

如果您担心
f
会得到一个额外的参数(因此可能会被错误地调用),有一种更复杂的方法涉及将闭包用作“函数工厂”:

在循环中使用
f=make_f(i)
而不是
def
语句。

解释 这里的问题是,创建函数
f
时,不会保存
i
的值。相反,
f
在调用时查找
i
的值

如果你仔细想想,这种行为是完全有道理的。事实上,这是函数工作的唯一合理方式。假设您有一个访问全局变量的函数,如下所示:

def f(i=i):
    return i
global_var = 'foo'

def my_function():
    print(global_var)

global_var = 'bar'
my_function()
当您阅读此代码时,您当然希望它打印“bar”,而不是“foo”,因为函数声明后,
global\u var
的值发生了变化。同样的事情也发生在您自己的代码中:当您调用
f
时,
i
的值已更改并设置为
2

解决方案 其实有很多方法可以解决这个问题。以下是一些选项:

  • i
    用作默认参数,强制其早期绑定 与闭包变量(如
    i
    )不同,定义函数时会立即计算默认参数:

    for i in range(3):
        def f(i=i):  # <- right here is the important bit
            return i
    
        functions.append(f)
    
  • 使用函数工厂捕获闭包中
    i
    的当前值 问题的根源在于
    i
    是一个可以更改的变量。我们可以通过创建另一个保证永远不变的变量来解决这个问题,最简单的方法是闭包:

  • 使用
    functools.partial
    i
    的当前值绑定到
    f
    用于将参数附加到现有函数。在某种程度上,它也是一种功能工厂

    import functools
    
    def f(i):
        return i
    
    for i in range(3):    
        f_with_i = functools.partial(f, i)  # important: use a different variable than "f"
        functions.append(f_with_i)
    
警告:这些解决方案仅在为变量指定新值时有效。如果修改存储在变量中的对象,您将再次遇到相同的问题:

>>> i = []  # instead of an int, i is now a *mutable* object
>>> def f(i=i):
...     print('i =', i)
...
>>> i.append(5)  # instead of *assigning* a new value to i, we're *mutating* it
>>> f()
i = [5]
请注意,
i
即使我们将其转换为默认参数,仍然会发生变化!如果您的代码发生了变异
i
,则必须将
i
的副本绑定到您的函数,如下所示:

  • def(i=i.copy()):
  • f=f\u工厂(i.copy())
  • f_with_i=functools.partial(f,i.copy())

为了补充@Aran Fey的优秀答案,在第二个解决方案中,您可能还希望修改函数中的变量,该变量可以通过关键字
非局部
完成:

def f_factory(i):
    def f(offset):
      nonlocal i
      i += offset
      return i  # i is now a *local* variable of f_factory and can't ever change
    return f

for i in range(3):           
    f = f_factory(i)
    print(f(10))

作为对自己的提醒:你怎么知道如何解决这些问题?@alwbtc这主要是经验,大多数人在某个时候都会独自面对这些问题。你能解释一下为什么它会起作用吗?(您在循环中生成的回调上保存了我,参数始终是循环的最后一个参数,所以谢谢!)
def f_factory(i):
    def f(offset):
      nonlocal i
      i += offset
      return i  # i is now a *local* variable of f_factory and can't ever change
    return f

for i in range(3):           
    f = f_factory(i)
    print(f(10))