Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

strange Python function scope behavior

Tags:

python

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.

like image 705
Albert Avatar asked Jun 22 '12 22:06

Albert


People also ask

What is the scope of a function in Python?

Local (or function) scope is the code block or body of any Python function or lambda expression. This Python scope contains the names that you define inside the function. These names will only be visible from the code of the function.

What is a scope violation Python?

this error occurs when the interpreter fails to find that specific variable(x in this case) because of its scope.

What are the four scopes in Python?

You will learn about the four different scopes with the help of examples: local, enclosing, global, and built-in. These scopes together form the basis for the LEGB rule used by the Python interpreter when working with variables.

Is Python lexically scoped?

In Python, just as in SML, or (modern) Lisp, the body of a function is evaluated in the environment where it was defined. So, all three languages are lexically scoped.


1 Answers

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.

like image 78
glglgl Avatar answered Oct 19 '22 11:10

glglgl