what is a function object?

Michael Hudson mwh21 at cam.ac.uk
Wed Nov 10 16:29:15 CET 1999


zakons at eccelerate.com writes:

>   Michael Hudson <mwh21 at cam.ac.uk> wrote:
> 
> > it doesn't know a priori that the name "foo" is bound to a
> > function. So it looks the name "foo" up, and assuming it finds
> > something, attempts to call it with argument x. "Attempting to call
> > it" goes something like:
> >
> > if type(foo) is types.MethodType:
> >      apply(foo.im_func,(foo.im_self,x))
> > elif type(foo) is types.FunctionType:
> >      apply(foo,(x,))
> > elif hasattr(foo,'__call__'):
> >      apply(foo.__call__,(x,))
> 
> Thanks Michael. 

A pleasure.

> Your comment on the __call__ builtin method keyed me into Mark
> Lutz's index to Programming in Python. Buried in the way back on
> page 747, Closures in Python, he describes a class called 'counter'
> which implements __call__. What this does is make 'counter' both an
> object and a method! I believe this is what it means when folks say
> "functions are first class objects in python".

Well regular functions are objects too.

> The real question is still "OK, so why would I want to make a method
> (function) a full blown class (object)?" It seems like this makes sense
> when there is some desired side effect every time the method is called.
> (Is there a recognized pattern for this type of behavior?)

It's a way of associating state with a function. Lets have an example:
(This forms part of a snippet I wrote; see
http://tor.dhs.org/~zephyrfalcon/snippets/source/122.txt)

I was preprocessing some Python code; I wanted to replace

{{y!}}

with

y0,y1,y2,...,yn

for some n. So I used re to match this pattern:

re.sub('{{[^}]*}}',rep,line)

But what to pass in for `rep'? re.sub allows you to pass in a callback
that is passed a MatchObject describing the text to be replaced. So if
I knew n was always 2, say, I could define this:

def rep(match):
    text = m.group(0)[2:-2]
    return string.replace(text,'!','0') + "," + string.replace(text,'!','1')

but I don't; so I define

class Replacer:
    def __init__(self,n):
        self.n=n
    def replace(self,m):
        import string
        t=m.group(0)[2:-2]
        r=''
        for i in range(self.n):
            r=r+string.replace(t,'!',`i`)+','
        return r[:-1]

Then "re.sub('{{[^}]*}}',Replacer(n).replace,line)" does what I want
(NB: I could have called the `replace' method `__call__' and then
written `Replacer(n).replace' as `Replacer(n)'; that I didn't is a
personal bias of mine and makes no difference to the point).

Hmm, I wrote this code a fair old while ago so there are things I'd do
differently now and the example's a bit long - but I wanted to post an
example of when I'd found this technique actually useful.

HTH,
Michael




More information about the Python-list mailing list