Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python globals, locals, and UnboundLocalError

I ran across this case of UnboundLocalError recently, which seems strange:

import pprint

def main():
    if 'pprint' in globals(): print 'pprint is in globals()'
    pprint.pprint('Spam')
    from pprint import pprint
    pprint('Eggs')

if __name__ == '__main__': main()

Which produces:

pprint is in globals()
Traceback (most recent call last):
  File "weird.py", line 9, in <module>
    if __name__ == '__main__': main()
  File "weird.py", line 5, in main
    pprint.pprint('Spam')
UnboundLocalError: local variable 'pprint' referenced before assignment

pprint is clearly bound in globals, and is going to be bound in locals in the following statement. Can someone offer an explanation of why it isn't happy resolving pprint to the binding in globals here?

Edit: Thanks to the good responses I can clarify my question with relevant terminology:

At compile time the identifier pprint is marked as local to the frame. Does the execution model have no distinction where within the frame the local identifier is bound? Can it say, "refer to the global binding up until this bytecode instruction, at which point it has been rebound to a local binding," or does the execution model not account for this?

like image 949
cdleary Avatar asked Jan 01 '09 04:01

cdleary


People also ask

What is globals and locals in Python?

The built-in functions globals() and locals() returns the global and local symbols table respectively. Python interpreter maintains a data structure containing information about each identifier appearing in the program's source code.

What is difference between global and nonlocal in Python?

An important difference between nonlocal and global is that the a nonlocal variable must have been already bound in the enclosing namespace (otherwise an syntaxError will be raised) while a global declaration in a local scope does not require the variable is pre-bound (it will create a new binding in the global ...

What is UnboundLocalError Python?

The Python "UnboundLocalError: Local variable referenced before assignment" occurs when we reference a local variable before assigning a value to it in a function. To solve the error, mark the variable as global in the function definition, e.g. global my_var .

How do I fix UnboundLocalError local variables?

UnboundLocalError can be solved by changing the scope of the variable which is complaining. You need to explicitly declare the variable global. Variable x's scope in function printx is global. You can verify the same by printing the value of x in terminal and it will be 6.


2 Answers

Where's the surprise? Any variable global to a scope that you reassign within that scope is marked local to that scope by the compiler.

If imports would be handled differently, that would be surprising imho.

It may make a case for not naming modules after symbols used therein, or vice versa, though.

like image 195
Albert Visser Avatar answered Sep 21 '22 05:09

Albert Visser


Well, that was interesting enough for me to experiment a bit and I read through http://docs.python.org/reference/executionmodel.html

Then did some tinkering with your code here and there, this is what i could find:

code:

import pprint

def two():
    from pprint import pprint
    print globals()['pprint']
    pprint('Eggs')
    print globals()['pprint']

def main():
    if 'pprint' in globals():
        print 'pprint is in globals()'
    global  pprint
    print globals()['pprint']
    pprint.pprint('Spam')
    from pprint import pprint
    print globals()['pprint']
    pprint('Eggs')

def three():
    print globals()['pprint']
    pprint.pprint('Spam')

if __name__ == '__main__':
    two()
    print('\n')
    three()
    print('\n')
    main()

output:

<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Eggs'
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>

<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Spam'

pprint is in globals()
<module 'pprint' from '/usr/lib/python2.5/pprint.pyc'>
'Spam'
<function pprint at 0xb7d596f4>
'Eggs'

In the method two() from pprint import pprint but does not override the name pprint in globals, since the global keyword is not used in the scope of two().

In method three() since there is no declaration of pprint name in local scope it defaults to the global name pprint which is a module

Whereas in main(), at first the keyword global is used so all references to pprint in the scope of method main() will refer to the global name pprint. Which as we can see is a module at first and is overriden in the global namespace with a method as we do the from pprint import pprint

Though this may not be answering the question as such, but nevertheless its some interesting fact I think.

=====================

Edit Another interesting thing.

If you have a module say:

mod1

from datetime import    datetime

def foo():
    print "bar"

and another method say:

mod2

import  datetime
from mod1 import *

if __name__ == '__main__':
    print datetime.datetime.now()

which at first sight is seemingly correct since you have imported the module datetime in mod2.

now if you try to run mod2 as a script it will throw an error:

Traceback (most recent call last):
  File "mod2.py", line 5, in <module>
    print datetime.datetime.now()
AttributeError: type object 'datetime.datetime' has no attribute 'datetime'

because the second import from mod2 import * has overriden the name datetime in the namespace, hence the first import datetime is not valid anymore.

Moral: Thus the order of imports, the nature of imports (from x import *) and the awareness of imports within imported modules - matters.

like image 36
JV. Avatar answered Sep 24 '22 05:09

JV.