Python闭包的概念让你感到困惑吗?

3
在马克·卢茨(Mark Lutz)的《学习Python第5版》(ISBN:9781449355739,第17章:“作用域”,第518页,侧边栏:“为什么您会在意:自定义open”)中,有以下插图:
import builtins

def makeopen(id):
    original = builtins.open
    def custom(*kargs, **pargs):
        print('Custom open call %r:' % id , kargs, pargs)
        return original(*kargs, **pargs)
    builtins.open = custom

makeopen('spam')
F = open('script2.py')

makeopen('eggs')
F = open('script2.py')

预期输出:

Custom open call 'spam': ('script2.py',) {}
Custom open call 'eggs': ('script2.py',) {} 

实际输出:

Custom open call 'spam': ('script2.py',) {}
Custom open call 'eggs': ('script2.py',) {} 
Custom open call 'spam': ('script2.py',) {}

我对闭包的理解是,它们应该返回多个每次调用都可更改的数据副本(例如其他语言中的实例变量)。
那么为什么“spam”会打印两次?
我使用PyCharm调试器逐步执行代码,但仍然不理解它。
这是因为变量original指向内置范围中的对象而不是封闭范围吗?
更新:
我认为问题在于第二次调用makeopen()时,变量original递归地指向custom()。也许最初是作为“特性”来使用的 :/ ……但我倾向于认为这是一个糟糕的示例。
以下是符合预期的解决方案:
import builtins

def makeopen(id):
    def custom(*kargs, **pargs):
        print('Custom open call %r:' % id , kargs, pargs)
        return builtins.open(*kargs, **pargs)
    return custom

file = 'script2.py'

f = makeopen('spam')
f(file)

g = makeopen('eggs')
g(file)

注意:上述解决方案实际上并不改变builtins.open,而是充当一个包装器。


“return multiple-copy data”是什么意思? - deceze
@deceze - 我已经重新表述成“每次调用可以更改的多个副本数据”(正如书中所使用)。我相信它的意思是Python封闭函数中的局部变量就像Java等其他语言中的实例变量。 - mvanle
一种“修复”此问题的方法是编写 def makeopen(id, original=builtins.open)。这将保存实际原始打开方式。 - Mateen Ulhaq
1个回答

4

在执行 makeopen('spam') 之后,open 是一个打印“spam”并打开文件的函数。 在执行 makeopen('eggs') 之后,open 现在是一个打印“eggs”,然后调用一个打印“spam”并打开文件的函数。

你正在逐步包装 open 函数,最终得到:

print("eggs")
↳ print("spam")
  ↳ open(...)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接