zy8818 发表于 2022-9-26 20:25:59

python内嵌函数闭包问题,栈空间是否释放

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


a=Funx(5)#执行到这里的时候Funx的栈空间已经退出了把,返回Funy的对象有什么用
#我假设python作者这里设计的有闭包栈空间没有退出等闭包执行完在退出,如果我不执行a(9)的话,栈空间是不是一直卡在那里
#我感觉这个设计是很有问题的
#我想知道这个代码怎么调试反汇编代码,我想看看如何实现的
a(9)

jackz007 发表于 2022-9-26 21:22:20

      一旦明白了这个函数的用法,就不会再有此疑问了
def Funx(x):
    def Funy(y):
      return x*y
    return Funy

A = Funx(3)
B = Funx(5)
print(A(6))# 3 x 6
print(A(8))# 3 x 8
print(B(6))# 5 x 6
print(B(8))# 5 x 8

      你想看反汇编代码,我也想,可是,我们都想多了,残酷的事实是,Python 是脚本语言,是逐条文本解释执行的,怎么看?

kogawananari 发表于 2022-9-26 21:30:11

栈空间不会释放 除非A的引用消失 可以使用函数的魔术方法__closure__
def foo():
    step = 0
    def add1():
      nonlocal step
      step += 1
      print(step)
    return add1


a=foo()

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

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

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

zy8818 发表于 2022-9-26 21:50:50

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

明白了执行三次a()栈空间的数据一直都在,不然栈空间释放了就会是3次结果都是1,编译器也会在后面的代码不再使用a()时释放栈空间,一切根据编译前的代码来计算好释放时间

Brick_Porter 发表于 2022-9-26 22:00:07

栈空间确实被释放了,栈是系统维护的,函数执行完毕了就释放内存由不得我们。但是内部定义的函数对象Funy在Funx返回时逃逸到了堆中。只要还有变量指向Funy对象(换言之还有对Funy的引用)堆上的空间就不会释放。

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

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

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

x = funa()# a 与 funb一起存储到堆上
x()# 只能通过这种形式修改局部变量a的值
页: [1]
查看完整版本: python内嵌函数闭包问题,栈空间是否释放