22

Question are at the end of this post.

First snippet: empty local variable dictionary.

def outer():
    x = 1
    def inner():
        print "Local variables: %s" % locals()
    return inner()
print outer()

Output: Local variables: {}

Second snippet: print inside inner() function and creating local variable entry.

def outer():
    x = 1
    def inner():
        print x
        print "Local variables: %s" % locals()
    return inner()
print outer()

Output:

1
Local variables: {'x': 1}

Third Snippet: del x from inside the inner function:

def outer():
    x = 1
    def inner():
        print x
        print "Local variables: %s" % locals()
        del x
    return inner()
print outer()

Output:

>>> outer()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in outer
  File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'x' referenced before assignment
>>>

Questions :

  1. In Second Snippet, how print statement create local variable.
  2. If it creates local variable inside inner function why I am not able to delete it.

Could someone please help me understanding this.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
James Sapam
  • 16,036
  • 12
  • 50
  • 73
  • 1
    A quick search revealed a duplicate question, but the answers for it aren't very good. Some of the information in the answers is outright wrong. I'm not voting to close. – user2357112 Jan 13 '14 at 12:17
  • 4
    From the [Python documentation](http://docs.python.org/2/library/functions.html#locals): "Free variables are returned by [`locals()`](http://docs.python.org/2/library/functions.html#locals) when it is called in function blocks, but not in class blocks." – user2357112 Jan 13 '14 at 12:21
  • possible duplicate of [Short Description of Python Scoping Rules](http://stackoverflow.com/questions/291978/short-description-of-python-scoping-rules) – Martijn Pieters Jan 13 '14 at 12:26
  • thank you @martijn, got lots of info from that link too. – James Sapam Jan 13 '14 at 12:31
  • Somewhere there's a great article by… I think Raymond Hettinger?… that explains how Python closures work in detail. Unfortunately, I can't remember where. And there are a lot of really bad articles that turn up in a Google search. – abarnert Jan 13 '14 at 12:38
  • Thank you @abarnet if you found out any please comment it, that will be very helpful for myself and many others who are new to the language. – James Sapam Jan 13 '14 at 12:42
  • 1
    @abarnert This [one](http://eli.thegreenplace.net/2010/09/18/python-internals-symbol-tables-part-1/) by Eli Bendersky perhaps. – Ashwini Chaudhary Jan 13 '14 at 14:45
  • @AshwiniChaudhary: That's not the one I was looking for. But that's a pretty impressive series, which covers closures along with all kinds of other things, and does so in a systematic way rather than jumping right in to the bytecode and how it uses values from the function and code objects. So it might be even more useful than the one I can't find. +1, and I'll copy your link to my answer for future searchers who don't like to read comments. – abarnert Jan 13 '14 at 19:36

3 Answers3

23

In Python, unless you specify otherwise (with a global statement, or a nonlocal statement in 3.0+), a variable is in locals if you modify it (assign to it, del it, etc.) anywhere in the function.*

In the first snippet, you never modify x, or even access it, so it's not local. In fact, it doesn't even exist. That's easy.

The second version is the tricky one. x is not local to inner, because you don't modify it in inner. So, Python goes looking for it, moving outward scope by scope, until it finds it a scope that has that variable. And it finds it as a local variable in outer. Which means it's a closure variable or free variable in inner. Since the locals function includes closure variables as well as local variables, you see it.

The third version, by doing del x, makes x local to inner.** So, it appears in locals. However, you try to print it without having ever assigned anything to it, so there is no value yet. So you get an UnboundLocalError.

Generally, once you understand the basic idea Python is trying to accomplish here, it's usually obvious what kind of variable you have. But if it's ever unclear, the detailed rules are defined in Naming and Binding.


If you want to understand how closures work under the covers, you can start by inspecting the function objects. Try this:

def outer():
    x = 1
    def inner():
        print x
        print "Local variables: %s" % locals()
    return inner
inner = outer()
print inner.func_closure
print inner.func_code.co_freevars
print outer.func_code.co_cellvars

The inspect module docs list all of the important members of function, code, and other "under the covers" objects.

Using the dis module to look at the bytecode for outer and inner may also be helpful.*** For example, if you run this code, you'll see a LOAD_FAST for the local, LOAD_DEREF for the cell, and LOAD_GLOBAL for the global.

But if you really want to understand how all of this really works, the series of articles on symbol tables at Eli Bendersky's "Python internals" blog covers just about everything very nicely. (Thanks to Ashwini Chaudhary for locating it and pointing it out in a comment.)


* This is checked at compile time, not execution time, so trying to confuse it with, e.g., exec can successfully confuse both Python and yourself.

** Note that del counts as both an modification and an access. This can be surprising, but you can see that def foo(): del x will raise an UnboundLocalError because the del makes x local, and the very same del fails to find a value.

*** … assuming you're using a Python implementation that uses CPython-style bytecode, like CPython itself (of course) or PyPy.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • in the third snippet even i didn't print also got the same error when i tried to del. – James Sapam Jan 13 '14 at 12:22
  • @yopy: In that case, you're trying to delete a variable before you've created it, which is a different error, but not _that_ different. And you can see that without any of this fancy stuff: `def foo(): del x`. – abarnert Jan 13 '14 at 12:24
  • 1
    @Cilyan: `inner` is a closure. The fact that he calls the closure immediately and returns its value, instead of returning the closure itself, doesn't mean it's not a closure. – abarnert Jan 13 '14 at 12:25
  • @Cilyan: That is still a closure. The function `outer` closes over `inner`, no matter where you call it. – Martijn Pieters Jan 13 '14 at 12:25
  • Oops, you're right. I was fooled by the code calling MAKE_FUNCTION instead of MAKE_CLOSURE: http://www.pastebin.ca/2535506 – Cilyan Jan 13 '14 at 12:31
8

Python supports nested scopes, by looking at how variables are used at compile time. Variables that you assign to in a function (or bind to with an import in a function) are considered local, everything else is non-local. Trying to delete a variable also marks it as a local.

Non-local names are searched for in parent scopes, and if not found are considered global.

In your second example, x refers to the name in the parent scope. You didn't assign to it, so it is a nested name, and can be seen in the local namespace. It is not actually a local name, but a free variable; it's value is taken from the parent scope instead.

In your last example, you try to delete x, making it a local name. Trying to reference it before anything was assigned to it results in an exception.

This is all documented in the Execution model documentation of the Python reference. Specifically:

When a name is used in a code block, it is resolved using the nearest enclosing scope. The set of all such scopes visible to a code block is called the block’s environment.

If a name is bound in a block, it is a local variable of that block. If a name is bound at the module level, it is a global variable. (The variables of the module code block are local and global.) If a variable is used in a code block but not defined there, it is a free variable.

The following constructs bind names: formal parameters to functions, import statements, class and function definitions (these bind the class or function name in the defining block), and targets that are identifiers if occurring in an assignment, for loop header, in the second position of an except clause header or after as in a with statement. The import statement of the form from ... import * binds all names defined in the imported module, except those beginning with an underscore. This form may only be used at the module level.

A target occurring in a del statement is also considered bound for this purpose (though the actual semantics are to unbind the name). It is illegal to unbind a name that is referenced by an enclosing scope; the compiler will report a SyntaxError.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

there is an explanation Why am I getting an UnboundLocalError when the variable has a value? on the python docs faq page, similar to the above answers by abarnert and Martijn.

zhangxaochen
  • 32,744
  • 15
  • 77
  • 108
  • thanks for the post but its bit different from what I was looking, because there is not local assignment in my snippet. – James Sapam Jan 14 '14 at 03:51