UnboundLocalError with extra code after return

Rich Healey healey.rich at gmail.com
Wed Sep 30 00:37:16 EDT 2009


On Sep 30, 2:15 pm, Rich Healey <healey.r... at gmail.com> wrote:
> I'm trying to write a decorator that causes a function to do nothing
> if called more than once- the reason for this is trivial and to see if
> I can (Read- I'm enjoying the challenge, please don't ruin it for me
> =] )
>
> However I'm getting strange results with Python 2.6.2 on win32.
>
> With this code:
>
> def callonce(func):
>     def nullmethod(): pass
>     def __():
>         return func()
>     return __
> @callonce
> def t2():
>     print "T2 called"
> t2()
>
> It does exactly what you'd expect, prints "T2 called"
>
> However:
>
> def callonce(func):
>     def nullmethod(): pass
>     def __():
>         return func()
>         func = nullmethod
>         return ret
>     return __
>
> @callonce
> def t2():
>     print "T2 called"
> t2()
>
> Gives me:
>
> C:\tmp\callonce>callonce.py
> Traceback (most recent call last):
>   File "C:\tmp\callonce\callonce.py", line 27, in <module>
>     t2()
>   File "C:\tmp\callonce\callonce.py", line 12, in __
>     return func()
> UnboundLocalError: local variable 'func' referenced before assignment
>
> Any ideas on why? This looks like a bug to me, but I'm quite new to
> this style of programming so it may be some nuance I'm not aware of.
>
> Thanks in advance.
>
> Rich Healey

In case anyone was paying attention I've now gotten this working- and
raised a whole bunch more questions!

def callonce(func):
    func.__RECALL = True
    def __():
        if func.__RECALL:
            func.__RECALL = False
            return func()
        else:
            return
    return __

@callonce
def t2():
    print "T2 called"
t2()
t2()
t2()

Works as expected- the last two t2() calls do nothing.

It seems that my problem was that I can't assign a new function to the
name func within the callonce() function. I can however interact with
the func object (in this case storing information about whether or not
I'd called it in it's __RECALL item.

Is there a cleaner solution?

I'd originally wanted to define a function that does nothing, and then
point the function to that for subsequent calls (this seems cleaner
overall, if a little longwidned to write)

ie:
def onlyCalledOnce():
    global onlyCalledOnce
    def nullfunc(): pass
    onlyCalledOnce = nullfunc
#   Do stuff here
    return "Something"

I'll keep plugging at this for a little while I think- thoughts
suggestions welcome!



More information about the Python-list mailing list