对Python闭包感到困惑

时间:2019-02-04 10:22:27

标签: python

在Mark Lutz撰写的“ Learning Python 5th Edition”(ISBN:9781449355739,第17章:“ Scopes”,第518页,边栏:“ Why You Will Care:Customizing open”)中,有以下插图:< / p>

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',) {}

我对闭包的理解是,它们应该返回多个副本每次调用可变数据(例如,其他语言的实例变量)。

那为什么“垃圾邮件”打印两次?

我已经使用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,而是充当包装器。

1 个答案:

答案 0 :(得分:4)

makeopen('spam')之后,open是一个打印“垃圾邮件”然后打开文件的功能。 在makeopen('eggs')之后,open现在是一个打印“鸡蛋”的函数,然后调用一个打印“垃圾邮件”然后打开文件的函数。

您将open函数依次包装在越来越多的层中,最后得到:

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