【问题标题】:Python, loops and closuresPython,循环和闭包
【发布时间】:2019-06-14 18:45:30
【问题描述】:

我是一位经验丰富的 C/C++(在某种程度上是 Java)程序员。我正在学习 python,但我对该语言的一些奇怪的(对于我的背景)行为感到困惑。

我正在学习嵌套函数和闭包(阅读“学习 Python”,这对我来说似乎是一个非常好的资源)。

我知道,如果我在调用创建的函数时将 def 嵌套在 for 循环中,它会查找捕获的循环变量的最后一个值(因为它通过引用捕获,正如 C++ 程序员所说的那样)

funcs = []
for i in range(4):
    def f():
        print(i)
    funcs.append(f)

运行程序的结果是

>>> for f in funcs:
      f()


3
3
3
3

现在,当我偶然发现这个(在我看来是什么)不一致时,我正在思考这个问题:如果我这样做

for i in range(4):
  funcs[i]()


0
1
2
3

更莫名其妙,如果我这样做了

>>> i = 2
>>> funcs[i]()

2

现在,列表中的所有函数都返回 2:

for f in funcs:
  f()


2
2
2
2

一定有一些我无法理解的范围相关问题

【问题讨论】:

  • 由于缩进错误,您发布的第一个代码无法正常工作。
  • 对不起,这是一个复制粘贴错误,我的代码是正确的,现在编辑它
  • 我认为你搞砸了funcs.append(f) 上的缩进。我在这里可能有点不对劲,但print(i) 将打印最后生成的i。在您的情况下,那是 for i in range(4): 而不是您定义函数时。所以你得到那个输出并不奇怪。所以i 在函数创建时不会被冻结,但它是一个实时值。您正在打印 i 的最后一个已知/设置值。
  • 如果我按照您现在编辑的方式运行程序,输出只是一个“3”...
  • 我也无法重现第二个示例。请修正缩进并编写完整的代码,以便我们为您提供帮助!

标签: python function closures definition


【解决方案1】:

你的功能

def f():
    print(i)

打印i的当前值。

如果你写

for i in range(4):
    funcs[i]()

然后i 被设置为0,1,2,3,因为你通过循环。这就是for i in range(4) 的意思。

如果你写

for f in funcs:
    f()

然后i 继续使用它已有的任何值。

【讨论】:

    【解决方案2】:

    首先,这会创建一个包含四个函数的列表。

    funcs = []
    for i in range(4):
      def f():
        print(i)
      funcs.append(f)
    

    这些函数中的每一个都查找i 的值,然后将其打印出来。

    这会遍历函数列表并调用每个函数:

    >>> for f in funcs:
          f()
    

    如上所述,这些函数查找 i,由于之前完成的 for i in range(4) 循环,现在为 3,因此您得到 3 的四个打印输出。

    现在你再次循环,使用i 作为循环变量:

    for i in range(4):
      funcs[i]()
    
    
    0
    1
    2
    3
    

    第一次循环时,i 是 0,所以当函数查找 i 时,它会得到 0,然后打印出来。然后它变为 1,然后是 2,然后是 3。

    以下代码只是以另一种方式更改i,并调用一个函数:

    >>> i = 2
    >>> funcs[i]()
    
    2
    

    您可以调用任何这些函数,但它们仍然会打印 2,因为这就是 i 现在的值。你只是迷路了,因为你循环了range(4)来创建这些函数,然后你循环了range(4)来索引函数列表,你继续重用i,然后你重新分配i并使用它来索引列表。

    如果您希望将每个函数的打印值i 固定为定义函数时的值,最简单的方法是使用默认参数,因为在定义函数时会评估这些参数,而不是当它被调用时:

    funcs = []
    for i in range(4):
      def f(num=i):
        print(num)
      funcs.append(f)
    

    【讨论】:

      【解决方案3】:

      这里没有矛盾。 f()i 的值取决于父作用域中i 的值。在您运行第一个for i in range(4) 之后,i 的值是range 中最后一项的值,即 3,因此所有后续对f() 的调用都将打印 3

      如果你运行

      for i in range(4):
        funcs[i]()
      

      您在每个迭代步骤中重新定义i 的值,因此您得到0,1,2,3 作为f 打印的值。正在做

      for x in range(4):
         funcs[x]()
      

      不会影响i 的值,因此您将在所有函数调用中将3 作为i 的值

      【讨论】:

        【解决方案4】:

        为了完整起见,这是一个替代实现:

        def getfunc(i):
            return lambda: i
        
        funcs = []
        for i in range(5):
            funcs.append(getfunc(i))
        
        for item in funcs:
            print(item())
        

        【讨论】:

          猜你喜欢
          • 2012-10-13
          • 2018-05-02
          • 2017-12-18
          • 2010-09-26
          • 2022-08-17
          • 1970-01-01
          • 2012-12-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多