3

Testing some Python code today I tried the following code:

(The following runs on Python 3.2+, although previous versions will raise a SyntaxError when del is used and a variable is referenced in an enclosing scope)

def x():
    N = 200
    def y():
            print(N)
    del N
    y()
x() 
NameError: free variable 'N' referenced before assignment in enclosing scope 

As you can see, Python does not raises NameError: global name 'N' is not defined, and that made me wondering about one thing which is:

when del statement used withing the enclosing scope of a function to delete a free variable that's used inside a nested function like y function here and thereafter if that free variable in our case N was deleted through del statement in the enclosing scope, what del actually does is to delete the name's (N) value and keeps it bound to the enclosing scope ?

A) Is that why it raises: NameError: free variable 'N' referenced before assignment... rather than NameError: global name 'N' is not defined?

B) How does del actually delete a name? Does it unbind its value and keep it in a scope ?

I found a similar case here

Community
  • 1
  • 1
GIZ
  • 4,409
  • 1
  • 24
  • 43
  • Python 2.7.3 raises `SyntaxError: can not delete variable 'N' referenced in nested scope` which actually makes more sense than what 3.2 is doing. – Dan D. Sep 25 '15 at 17:47
  • Sorry what is your first question? – Anand S Kumar Sep 25 '15 at 17:50
  • @DanD. Python 2.7 also catches the error when `x` is defined, rather than when `x` is called. I think it's related to the `nonlocal` statement added in Python 3, but I'm not sure how that would apply here (since `nonlocal` isn't used). – chepner Sep 25 '15 at 18:03
  • @l'L'l It's [illegal](https://docs.python.org/2/reference/simple_stmts.html?highlight=del#del) to use `del` statement in enclosing scope prior to 3.2+ – GIZ Sep 25 '15 at 18:09
  • @direprobs: Edited your question to make that point more clear :) – l'L'l Sep 25 '15 at 18:36

1 Answers1

1

It works like this: when you write print(N) inside y but do not assign to N in y, that means that at the time y is defined, Python looks in its enclosing scopes to find the nearest one where the name N is assigned. It doesn't matter whether the assignment to N happens before or after the definition of y, or whether it's later undone with a del; it just looks for the nearest enclosing scope where an assignment to N exists. In this case, since N is assigned inside x, y essentially makes a little note saying "when I need to get N, I will look in the scope of x".

So when you actually call y, it looks for N in the scope of x. If it isn't there, you get the error. Note that you get this same error without del if you move the N = 100 line after the call to y.

BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • "Note that you get this same error without `del` if you move the `N = 200` line after the call to `y`" That's technically fine ! As the control flow didn't reach the assignment yet, so it's absolutely correct to raise that error. But in my case, the control flow reached the assignment and it evaluated it, but then `del` deletes the `N` "supposedly" completely. But it seems the `del` stmt just unbounded its value 200 and kept it in the enclosing scope – GIZ Sep 25 '15 at 18:05
  • @direprobs: It is as I said. If you assign to `N` *anywhere* in the body of `x`, that irrevocably marks `x` as "a scope where N is defined", and it means `y` will look for `N` in the scope of `x`. It doesn't matter whether `N` is actually there at the time `y` looks for it, and it doesn't matter whether the reason it isn't there is that the assignment hasn't been reached, or the assignment was made but then the name was deleted. There's no difference between a name that was assigned and then deleted and a name that hasn't been assigned yet. – BrenBarn Sep 25 '15 at 18:10
  • So technically speaking, when the compiler compiles the function, it binds the `N` to the enclosing scope and when it's deleted and `y` trys to use `N` the interpreter looks for it in the enclosing scope and it finds it, however, unassigned. That's why it raises the `NameError: free variable 'N' referenced before assignment...` error ? – GIZ Sep 25 '15 at 18:21
  • No, it looks for it in the enclosing scope, and does not find it, hence raises the error. – Anand S Kumar Sep 25 '15 at 18:26
  • @direprobs: More or less, although I wouldn't use the word "bind" as that usually refers to the binding of a value to a name. But yes, as I said in my answer, it "marks" `N` as being assigned in the scope of `x`, which means `y` will always look for `N` in `x`, even if it's later deleted. The decision of "where do I look for `N`" is made when `y` is *defined*, but whether `N` is actually there when needed is not known until `y` is *called*. – BrenBarn Sep 25 '15 at 18:27