Why aren't python nested functions called closures?

后端 未结 8 602
情歌与酒
情歌与酒 2020-11-22 05:37

I have seen and used nested functions in Python, and they match the definition of a closure. So why are they called nested functions instead of closures<

8条回答
  •  南笙
    南笙 (楼主)
    2020-11-22 06:16

    Python has a weak support for closure. To see what I mean take the following example of a counter using closure with JavaScript:

    function initCounter(){
        var x = 0;
        function counter  () {
            x += 1;
            console.log(x);
        };
        return counter;
    }
    
    count = initCounter();
    
    count(); //Prints 1
    count(); //Prints 2
    count(); //Prints 3
    

    Closure is quite elegant since it gives functions written like this the ability to have "internal memory". As of Python 2.7 this is not possible. If you try

    def initCounter():
        x = 0;
        def counter ():
            x += 1 ##Error, x not defined
            print x
        return counter
    
    count = initCounter();
    
    count(); ##Error
    count();
    count();
    

    You'll get an error saying that x is not defined. But how can that be if it has been shown by others that you can print it? This is because of how Python it manages the functions variable scope. While the inner function can read the outer function's variables, it cannot write them.

    This is a shame really. But with just read-only closure you can at least implement the function decorator pattern for which Python offers syntactic sugar.

    Update

    As its been pointed out, there are ways to deal with python's scope limitations and I'll expose some.

    1. Use the global keyword (in general not recommended).

    2. In Python 3.x, use the nonlocal keyword (suggested by @unutbu and @leewz)

    3. Define a simple modifiable class Object

    class Object(object):
        pass
    

    and create an Object scope within initCounter to store the variables

    def initCounter ():
        scope = Object()
        scope.x = 0
        def counter():
            scope.x += 1
            print scope.x
    
        return counter
    

    Since scope is really just a reference, actions taken with its fields do not really modify scope itself, so no error arises.

    4. An alternative way, as @unutbu pointed out, would be to define each variable as an array (x = [0]) and modify it's first element (x[0] += 1). Again no error arises because x itself is not modified.

    5. As suggested by @raxacoricofallapatorius, you could make x a property of counter

    def initCounter ():
    
        def counter():
            counter.x += 1
            print counter.x
    
        counter.x = 0
        return counter
    

提交回复
热议问题