Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional local import exception

Tags:

python

Consider the snippet

import something
import sys
print(sys.version)

def f(a):
    b = a
    if a==1:
        import something
    b *= something.value # <<<<<
    return b

print(f(1))
print(f(2))

where the module something defines value = 1. Running that script with Python 3.6.7, the call f(1) succeeds but the call f(2) fails with an exception:

UnboundLocalError: local variable 'something' referenced before assignment

at the line marked with <<<<<. I really don't understand that. I found the same issue with Python 2.7, so I would bet this is not a simple regression but really tied to the way Python handles such local imports. I could not find any hint in the documentation. Has anybody an explanation?

like image 720
frapadingue Avatar asked Jan 01 '23 10:01

frapadingue


2 Answers

An import x statement does two things*

  1. It loads (and runs) the module being referenced if it hasn't already been
  2. It assigns the module to a variable of the same name

Your function only assigns the module something to the variable something when a == 1. If a != 1, even if the module has been loaded, it is not assigned to a variable, so cannot be referenced.

This is why what you're doing fails. It's also why what you're trying to do provides you with absolutely no advantage because modules are only loaded and run the first time they are imported. Every subsequent time is just doing step 2.

The reason your code is not referencing the global something is because python overrides global names if a local variable of the same name appears anywhere in a function, even if it is never reached. The following function f fails similar to how yours does.

x = 1
def f(y):
    if False:
       x = y
    return x

The solution is to not use x as both a local and global variable.

like image 66
FHTMitchell Avatar answered Jan 15 '23 02:01

FHTMitchell


If there is a assignment to a variable in a given scope, the variable is local to that scope. If that assignment is conditional and you can run your code such as it never happens (or it is referenced in that scope after being assigned to), you get this very exception. Smaller example of the same:

def f():
    if False:
        a = 1
    print(a)

a = 1
f()

And the obligatory FAQ link.

like image 42
Ondrej K. Avatar answered Jan 15 '23 03:01

Ondrej K.