首页 > 解决方案 > 对 Python 闭包感到困惑

问题描述

在 Mark Lutz 的“Learning Python 5th Edition”中——(ISBN:9781449355739,第 17 章:“Scopes”,第 518 页,侧边栏:“Why You Will Care: Customizing 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',) {}

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

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

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

标签: python

解决方案


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

您不断地将open函数包装在越来越多的层中,最终得到:

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

推荐阅读