鱼C论坛

 找回密码
 立即注册
查看: 2005|回复: 4

[已解决]python内嵌函数闭包问题,栈空间是否释放

[复制链接]
发表于 2022-9-26 20:25:59 | 显示全部楼层 |阅读模式

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

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

x
def Funx(x):
    def Funy(y):
        return x*y
    return Funy


a=Funx(5)#执行到这里的时候Funx的栈空间已经退出了把,返回Funy的对象有什么用
#我假设python作者这里设计的有闭包栈空间没有退出等闭包执行完在退出,如果我不执行a(9)的话,栈空间是不是一直卡在那里
#我感觉这个设计是很有问题的
#我想知道这个代码怎么调试反汇编代码,我想看看如何实现的
a(9)
最佳答案
2022-9-26 21:30:11
栈空间不会释放 除非A的引用消失 可以使用函数的魔术方法__closure__
  1. def foo():
  2.     step = 0
  3.     def add1():
  4.         nonlocal step
  5.         step += 1
  6.         print(step)
  7.     return add1


  8. a=foo()

  9. a()
  10. print(list(map(lambda b:b.cell_contents, a.__closure__)))

  11. a()
  12. print(list(map(lambda b:b.cell_contents, a.__closure__)))

  13. a()
  14. print(list(map(lambda b:b.cell_contents, a.__closure__)))
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-9-26 21:22:20 | 显示全部楼层
        一旦明白了这个函数的用法,就不会再有此疑问了
  1. def Funx(x):
  2.     def Funy(y):
  3.         return x*y
  4.     return Funy

  5. A = Funx(3)
  6. B = Funx(5)
  7. print(A(6))  # 3 x 6
  8. print(A(8))  # 3 x 8
  9. print(B(6))  # 5 x 6
  10. print(B(8))  # 5 x 8
复制代码


        你想看反汇编代码,我也想,可是,我们都想多了,残酷的事实是,Python 是脚本语言,是逐条文本解释执行的,怎么看?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-9-26 21:30:11 | 显示全部楼层    本楼为最佳答案   
栈空间不会释放 除非A的引用消失 可以使用函数的魔术方法__closure__
  1. def foo():
  2.     step = 0
  3.     def add1():
  4.         nonlocal step
  5.         step += 1
  6.         print(step)
  7.     return add1


  8. a=foo()

  9. a()
  10. print(list(map(lambda b:b.cell_contents, a.__closure__)))

  11. a()
  12. print(list(map(lambda b:b.cell_contents, a.__closure__)))

  13. a()
  14. print(list(map(lambda b:b.cell_contents, a.__closure__)))
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-9-26 21:50:50 | 显示全部楼层
kogawananari 发表于 2022-9-26 21:30
栈空间不会释放 除非A的引用消失 可以使用函数的魔术方法__closure__

明白了执行三次a()栈空间的数据一直都在,不然栈空间释放了就会是3次结果都是1,编译器也会在后面的代码不再使用a()时释放栈空间,一切根据编译前的代码来计算好释放时间
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-9-26 22:00:07 | 显示全部楼层
栈空间确实被释放了,栈是系统维护的,函数执行完毕了就释放内存由不得我们。但是内部定义的函数对象Funy在Funx返回时逃逸到了堆中。只要还有变量指向Funy对象(换言之还有对Funy的引用)堆上的空间就不会释放。

第二个问题,返回的Funy对象有什么用。我们可以这样理解闭包:函数+上下文环境。我们试着调用这个函数看看
  1. def Funx(x):
  2.     def Funy(y):
  3.         return x*y
  4.     return Funy

  5. a = Funx(5)
  6. print(a(9))  # 5 * 9
  7. print(a(3))  # 5 * 3
复制代码
当我们通过Funx(5)返回一个函数时,由于其引用了外部的参数变量x,所以外部参数变量x与这个函数对象一起逃逸到了堆上。如此一来闭包便有了两个功能:
1. 延迟调用。调用外部函数之后不是直接返回结果而是返回一个闭包函数,需要二次调用才会产生结果。恰是这样的延迟然我们可以批量生成功能类似的函数
  1. def generate_func(a, b, c):
  2.     def func(x):
  3.         return a * x * x + b * x + c
  4.     return func
复制代码
这个闭包可以让我们批量生成一元二次函数。

2. 避免篡改。正如前面所说,闭包函数可以理解为函数+上下文环境。返回闭包函数时函数对象与其引用的上下文环境作为一个整体存储到了堆上,所以只有闭包函数才能修改上下文环境中的变量
  1. def funa():
  2.     a = 0
  3.     def funb():
  4.         nonlocal a
  5.         a += 1
  6.         print(a)
  7.     return funb

  8. x = funa()  # a 与 funb一起存储到堆上
  9. x()  # 只能通过这种形式修改局部变量a的值
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-7 02:00

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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