In "Learning Python 5th Edition" by Mark Lutz - (ISBN: 9781449355739, chapter 17: "Scopes", p. 518, sidebar: "Why You Will Care: Customizing open") - there is the following illustration:
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') Expected output:
Custom open call 'spam': ('script2.py',) {} Custom open call 'eggs': ('script2.py',) {} Actual output:
Custom open call 'spam': ('script2.py',) {} Custom open call 'eggs': ('script2.py',) {} Custom open call 'spam': ('script2.py',) {} My understanding of closures is that they are supposed to return multiple copy per-call changeable data (ie. like instance variables in other languages).
So why is "spam" printed twice ?
I have stepped through the code with the PyCharm debugger and I still do not understand it.
Is it because the variable original points to an object in the built-in scope instead of an enclosing scope ?
UPDATE:
I think the problem was that on the 2nd call to makeopen(), the variable original recursively points to custom(). Maybe it was originally intended as a "feature" :/ ... but I am inclined to think it is a terrible example.
Here's a solution that works as expected:
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) Note: The above solution does not actually change builtins.open but instead acts as a wrapper.
def makeopen(id, original=builtins.open). This will save the actual original open.