Albert
Albert

Reputation: 68140

strange Python function scope behavior

I'm confused about this scope behavior:

class Bar:
    def __init__(self):
        for fn in ["open","openW","remove","mkdir","exists","isdir","listdir"]:
            print "register", fn
            def func_wrapper(filename):
                print "called func wrapper", fn, filename
            setattr(self, fn, func_wrapper)

bar = Bar()
bar.open("a")
bar.remove("b")
bar.listdir("c")

This gives the output:

register open
register openW
register remove
register mkdir
register exists
register isdir
register listdir
called func wrapper listdir a
called func wrapper listdir b
called func wrapper listdir c

But I would have expected that func_wrapper would always be the correct function. I know that the scope of func_wrapper is to the whole function but I redefine it in every loop iteration and the last instance got saved away in the attrib. I also tried to add func_wrapper = None below the setattr but that doesn't help (would also have wondered me...).

Am I blind? I don't even really see how to work around / fix this.

Upvotes: 6

Views: 190

Answers (1)

glglgl
glglgl

Reputation: 91017

Either with

class Bar:
    def __init__(self):
        for fn in ["open","openW","remove","mkdir","exists","isdir","listdir"]:
            print "register", fn
            def func_wrapper(filename, fn=fn):
                print "called func wrapper", fn, filename
            setattr(self, fn, func_wrapper)

or, more robustly, with

def mkwrapper(fn):
    def func_wrapper(filename):
        print "called func wrapper", fn, filename
    func_wrapper.__name__ = fn
    return func_wrapper

class Bar:
    def __init__(self):
        for fn in ["open","openW","remove","mkdir","exists","isdir","listdir"]:
            print "register", fn
            func_wrapper = mkwrapper(fn)
            setattr(self, fn, func_wrapper)

In your original example, all generated functions access the same outer variable fn, which changes in every loop run. In the corrected examples, this is prevented.

Upvotes: 6

Related Questions