鱼C论坛

 找回密码
 立即注册
查看: 135|回复: 3

关于Python课程中关于闭包函数的困惑

[复制链接]
发表于 2026-2-8 22:16:19 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
小甲鱼老师在讲解闭包函数的时候,举以下的例子:

def outer():
    x = 0
    y = 0
    def inner(x1, y1):
        nonlocal x, y
        x += x1
        y += y1
        print(f"现在x={x}, y = {y}")
    return inner

我的困惑是在执行move = outer()后,运行move(1,2)后,外部函数作用域中的x、y函数的值是不是也都分别变成1、2了?

还有个问题,我如果想对上述案例进行修改,想实现在执行内部函数前、后时,外部函数作用域中的x、y值分别是多少?以及内部函数作用域中x1、y1的值分别是多少?我写的代码如下:

def outer():
    x = 0
    y = 0
    print(f"执行内部函数前,外部函数作用域中:x={x}, y = {y}")
    def inner(x1, y1):
        nonlocal x, y
        x += x1
        y += y1
        print(f"内部函数作用域中:x={x}, y = {y}")
    return inner
    print(f"执行内部函数后,外部函数作用域中:x={x}, y = {y}")

但执行后有2个问题:
1、执行move = outer()后,只显示“执行内部函数前,外部函数作用域中:x=0, y = 0”,并没有打印出来“执行内部函数后,外部函数作用域中:x=0, y = 0”。
2、执行move(1,2)后,只显示“内部函数作用域中:x=1, y = 2”,并没有执行外部函数作用域中的两个print语句。

麻烦大佬帮忙指点一下迷津,谢谢!

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2026-2-8 22:17:26 | 显示全部楼层
第一个问题的解答:

是的,执行 move = outer() 后,再运行 move(1,2),外部函数作用域中的 xy 的值会分别变成 1 和 2。这是因为 inner 函数使用了 nonlocal 关键字,使得对 xy 的修改作用于 outer 函数的作用域。闭包保留了这些变量的引用,所以每次调用 move 都会更新这些值。

第二个问题的解答:

你的代码有两个关键问题:

1. print(f"执行内部函数后,外部函数作用域中:x={x}, y = {y}") 位于 return inner 语句之后。在 Python 中,return 会立即结束函数,因此这行代码永远不会被执行。

2. 当执行 move(1,2) 时,你只是在调用 inner 函数,而 outer 函数中的 print 语句只在最初调用 outer() 时执行一次,不会在每次调用 inner 时重复执行。

修改建议:

如果你想在每次调用内部函数前后打印外部函数作用域的值,以及内部函数的参数,可以将打印语句放在 inner 函数内部。例如:


  1. def outer():
  2.     x = 0
  3.     y = 0
  4.     print(f"初始化外部函数作用域:x={x}, y={y}")
  5.     def inner(x1, y1):
  6.         nonlocal x, y
  7.         print(f"执行内部函数前,外部函数作用域:x={x}, y={y}")
  8.         print(f"内部函数参数:x1={x1}, y1={y1}")
  9.         x += x1
  10.         y += y1
  11.         print(f"执行内部函数后,外部函数作用域:x={x}, y={y}")
  12.     return inner

  13. move = outer()
  14. move(1,2)
复制代码


执行上述代码后,输出会显示:

  1. 初始化外部函数作用域:x=0, y=0
  2. 执行内部函数前,外部函数作用域:x=0, y=0
  3. 内部函数参数:x1=1, y1=2
  4. 执行内部函数后,外部函数作用域:x=1, y=2
复制代码


这样,你就可以清晰地看到每次调用内部函数时,外部函数作用域的值变化以及内部函数的参数值。注意,外部函数的初始化打印只执行一次,而内部函数中的打印会在每次调用时执行。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2026-2-9 21:43:01 | 显示全部楼层
FishC 发表于 2026-2-8 22:17
第一个问题的解答:

是的,执行 move = outer() 后,再运行 move(1,2),外部函数作用域中的 x 和 y 的值 ...


def outer():
    x = 0
    y = 0
    print(f"初始化外部函数作用域:x={x}, y={y}")
    def inner(x1, y1):
        nonlocal x, y
        print(f"执行内部函数前,外部函数作用域:x={x}, y={y}")
        print(f"内部函数参数:x1={x1}, y1={y1}")
        x += x1
        y += y1
        print(f"执行内部函数后,外部函数作用域:x={x}, y={y}")
    return inner

move = outer()
move(1,2)

在上面的代码中,如果把print(f"执行内部函数前,外部函数作用域:x={x}, y={y}")放在nonlocal x, y前面,系统会报语法错误“SyntaxError: name 'x' is used prior to nonlocal declaration”,这是为什么呢?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2026-2-9 21:43:49 | 显示全部楼层
LEEFEI571 发表于 2026-02-09 21:43
FishC 发表于 2026-2-8 22:17
第一个问题的解答:

...


这是一个很好的问题!这是因为 Python 对 nonlocal 的声明位置有严格的语法规定。

简单来说:**在一个函数内,如果你要对一个外层作用域的变量使用 nonlocal 声明,那么你必须在使用这个变量(无论是读取还是修改)之前,先完成这个声明。**

让我们分析一下你的代码场景:

def outer():
    x = 0
    y = 0
    def inner(x1, y1):
        # 错误!在声明nonlocal之前尝试读取 x 和 y
        print(f"执行前,外部x={x}, y={y}")
        nonlocal x, y # 声明来得太晚了
        x += x1
        y += y1

**为什么会这样?**

1.  Python 在编译函数定义时,需要确定函数内部每个变量(如 xy)的作用域。
2.  当编译器在 inner 函数中看到 print(f"… x={x}, y={y}") 时,它需要知道 xy 是来自哪里。
    *   它们是 inner 的局部变量吗?(显然不是,这里还没赋值)
    *   它们是来自外层 outer 作用域的变量吗?
3.  编译器在扫描到 nonlocal x, y 这一行之前,它**无法确定** xy 是外层变量。对于在函数内被引用的变量,如果后面没有特别的声明(如 nonlocalglobal),Python 可能会把它当作一个未定义的局部变量来处理,从而导致混淆和潜在的 bug。
4.  为了消除这种歧义,Python 语法强制要求:**如果你想使用 nonlocal,它的声明语句必须放在该函数块内所有对该变量的引用之前。** 这确保了编译器从一开始就知道这些变量不是当前函数的局部变量。

**总结一下规则:**
  • nonlocal 语句的作用是“将这个名字绑定到外层(非全局)作用域的一个已有变量上”。
  • 这个绑定操作必须在任何使用这个名字之前完成。
  • 这适用于对该变量的任何操作,包括读取其值(比如在 print 中)和修改其值(比如 x += x1)。


**正确的做法:**
所以,你必须把 nonlocal 声明放在内层函数所有代码的最前面。

def outer():
    x = 0
    y = 0
    print(f"初始化外部函数作用域:x={x}, y={y}")
    def inner(x1, y1):
        # 第一步:先声明
        nonlocal x, y
        # 第二步:之后才能安全地使用它们
        print(f"执行内部函数前,外部函数作用域:x={x}, y={y}")
        print(f"内部函数参数:x1={x1}, y1={y1}")
        x += x1
        y += y1
        print(f"执行内部函数后,外部函数作用域:x={x}, y={y}")
    return inner

这样一来,Python 编译器在开始处理 inner 函数体时,立刻就明白了:“哦,这里的 xy 不是本地新变量,我要去外层找它们。” 这样它就能正确地解析后续所有对 xy 的引用了。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2026-3-4 09:04

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表