Function decorator that caches function results

Fredrik Lundh fredrik at pythonware.com
Sun Oct 9 17:39:23 CEST 2005


Steven D'Aprano wrote:

> > 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.

they're hardly abstract; they all have distinct object implementations in the
Python runtime.  there's an include file and a main implementation file for each
one of them, and you can access the type objects for each one of them from
Python code.  any implementation of Python has to implement them.  there's
no such thing for "closures"; they're the sum of a lot of different parts, all of
which can be implemented in many different ways.

> 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.

having something isn't the same thing as being something.

if you replace "function" with "function object", you get a bit closer to the
truth.  e.g. in this snippet

    def wrapper(a):
        def func3():
            return a
        return func

"func" and "wrapper" are functions, but if you call wrapper twice, you get
two different function objects, each with it's own distinct closure.

(to see this in action, take the snippet I posted in an earlier post and
add

    f = wrapper(10); decode(f)
    f = wrapper(10); decode(f)

to the end.  this prints:

--------------------------------------------------------------------
func3:
  9           0 LOAD_DEREF               0 (a)
              3 RETURN_VALUE
              4 LOAD_CONST               0 (None)
              7 RETURN_VALUE
co_freevars = ('a',)
func_closure = (<cell at 0x0083EE50: int object at 0x00798250>,)

--------------------------------------------------------------------
func3:
  9           0 LOAD_DEREF               0 (a)
              3 RETURN_VALUE
              4 LOAD_CONST               0 (None)
              7 RETURN_VALUE
co_freevars = ('a',)
func_closure = (<cell at 0x0083EDB0: int object at 0x0079A960>,)

)

same function, same code, different closures.

(note that in CPython, functions only exist on the syntax level; when
you writing something that you think is a function, the CPython com-
piler will turn this into a code object and translate "def" to bytecode;
the actual function object isn't created until you execute those byte-
codes)

> 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.

function or function object?  all instances of the latter have a global en-
vironment (func_globals), some also have default arguments (func_defaults),
some also have references to nested scopes (func_freevars).  all of them
have all these attributes; that they're set to None when empty is just an
memory optimization.

> >>> 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.

func_closure is the name of an attribute.  it contains information about
a certain part of a function object's closure (free variables in an existing
outer scope), but, despite its name, it's not *the* closure.

> So no, there is no "abstract" difference between a closure and a raw
> function, but there is a practical difference.

only if you're obsessed with CPython implementation details.

</F>






More information about the Python-list mailing list