Function decorator that caches function results

Steven D'Aprano steve at REMOVETHIScyber.com.au
Sun Oct 9 11:07:34 EDT 2005


On Sun, 09 Oct 2005 14:27:32 +0200, Fredrik Lundh wrote:

> Steven D'Aprano wrote:
> 
>> Yes I did. Did you read my post?
> 
> given that the wikipedia page says
> 
>     "a closure is an abstraction representing a function, plus the
>     lexical environment (see static scoping) in which the function
>     was created."
> 
> and you're still focussing on type(function), it sure looks as if you missed
> certain parts of that explanation.
> 
> let's take it again, with emphasis on some important words:
> 
>     "a closure is an ABSTRACTION representing a function, PLUS the
>     lexical ENVIRONMENT (see static scoping) in which the function
>     was created."
> 
>> If you create a closure, using a memoization technique as per the original
>> post, and then call type() on that closure, Python reports <type 'function'>.
> 
> that's because "closure" is an abstract concept.

So are functions, modules, classes, instances, ints and strings.

> there is no "closure" object
> in Python, just as there is no "program" object.

Clearly there is no DISTINCT closure object. If there were, I wouldn't
need to ask how one can tell them apart, because type() would just report
that one was a function and one was a closure. I don't have a problem with
that. But read on...

> function objects always con-
> tain all the information they need about their context, and the sum of that is
> what forms the closure.

If what you say is true, then all functions are closures, and closure is
just a synonym for function, and there is no difference between a function
and a closure. Then why bother to create the term? Clearly, whoever
invented the term did so to distinguish the two.

At a practical, Python level, there is a difference between a function
before and after it gets made into a closure using, e.g. the original
poster's memoization technique. In Python at least, it is not true that a
function and a function-turned-into-closure is the same thing.

See, for example, this:-

>>> def closefy(fn):
...     def do_nothing(*args, **kwargs):
...             return fn(*args, **kwargs)
...     return do_nothing
...
>>> def spam1():
...     return 0
...
>>> spam2 = closefy(spam1)
>>> spam1()
0
>>> spam2()
0
>>> spam2 is spam1
False
>>> spam1.func_closure is None
True
>>> spam2.func_closure
(<cell at 0xf70a2734: function object at 0xf6e0a144>,)
>>> hex(id(spam1))
'0xf6e0a144'

In one case, the "raw" function has None stored in its func_closure
attribute. In the other, it has a tuple containing at least one object of
type cell. That cell object appears to contain a reference back to the
original "raw" function.

Now, I'll tell you what I think is going on: spam1() obviously has lexical
scope, but that scope doesn't need to be saved with the function because
it is implicitly understood by Python.

The output of closefy(), on the other hand, has scope different from
the "normal" Python scope. So its output has to package up enough
information to re-create it's lexical scope in a cell object, and
that gets stored in the output's func_closure attribute.

So no, there is no "abstract" difference between a closure and a raw
function, but there is a practical difference. Now perhaps in some other
languages there is no practical difference either, but (as far as I can
see) Python is not that language.

Am I close? 

Here are another two code snippets that show something of what Python is
doing:-

>>> def thingo():
...     def shrubbery():
...             return 0
...     return shrubbery
...
>>> spam3 = thingo()
>>> spam3()
0
>>> spam3.func_closure is None
True

>>> def thingy():
...     n = 0
...     def shrubbery():
...             return n
...     return shrubbery
...
>>> spam4 = thingy()
>>> spam4.func_closure
(<cell at 0xf70a2b24: int object at 0x901a158>,)


-- 
Steven.




More information about the Python-list mailing list