Python3 method not callable due to UnboundLocalError - python

Take into account the following code:
def main():
print('Calling methodA()')
methodA()
print('Calling methodB(True)')
methodB(True)
print('Calling methodB(False)')
try:
methodB(False)
except UnboundLocalError as error:
print(f'--> "UnboundLocalError" raised: {error}')
def methodA():
print('Running methodA()')
print('"method_original" in globals(): ' + str('method_original' in globals()))
method_original()
def methodB(patch_function):
print(f'Running methodB({patch_function})')
print('"method_original" in globals(): ' + str('method_original' in globals()))
if patch_function:
method_original=method_patched
method_original()
def method_original():
print('Running method_original()')
def method_patched():
print('Running method_patched()')
if __name__ == '__main__':
main()
It produces the following output:
Calling methodA()
Running methodA()
"method_original" in globals(): True
Running method_original()
Calling methodB(True)
Running methodB(True)
"method_original" in globals(): True
Running method_patched()
Calling methodA(False)
Running methodB(False)
"method_original" in globals(): True
--> "UnboundLocalError" raised: local variable 'method_original' referenced before assignment
Which makes no sense because "method_original" is in globals(). This error can be fixed simply adding global method_original at the beginning of the methodB() but in some cases we have a lot of functions and it could be a pain in the ass to put all of them at the beginning of every method.
Are there any rules to avoid this behavior?
//BR!

Let me explain it in a simpler example :
def fn(a):
if a % 2 == 0:
x = a
return x
print(fn(10)) # Fine
print(fn(9)) # UnboundLocalError: local variable 'x' referenced before assignment
In compile time, when interpreter reaches the function, it sees that there is an assignment to x, so it marks x as a "local" variable. Then in "runtime" interpreter tries to find it only in local namespace ! On the other hand, x is only defined, if a is even.
It doesn't matter if it presents in global namespace, now I want to add a global variable named x, to my example:
def fn(a):
if a % 2 == 0:
x = a
return x
x = 50
print(fn(10)) # Fine
print(fn(9)) # UnboundLocalError: local variable 'x' referenced before assignment
Nothing changed. Interpreter still tries to find x inside the function in local namespace.
Same thing happened in your example.
This is to show which variables are "local":
def fn(a):
if a % 2 == 0:
x = a
return x
print(fn.__code__.co_varnames)
co_varnames is a tuple containing the names of the local variables
(starting with the argument names)
Solution:
Either use global (which I see you don't like) , or do not do assignment inside the function, for example change your methodB to :
def methodB(patch_function):
print(f'Running methodB({patch_function})')
print('"method_original" in globals(): ' + str('method_original' in globals()))
if patch_function:
method_patched()
else:
method_original()

Related

Problem with accessing variable from function in another file

I need to get a variable declared in a function in another file.
Requirement is that I can't call the variable from setvar() function.
set.py:
def setvar():
global x
x = 100
def getvar():
return x
if __name__ == '__main__':
setvar()
getvar()
get.py:
import set
y = set.getvar()
print(y)
Error:
NameError: name 'x' is not defined
Why is x not defined? When I print it in getvar, x is defined.
How can I change it to get it work?
Why is x not defined?
The reason is because of your line:
if __name__ == '__main__':
As you know, that line means to only run the code within the if statement if the program is being run directly, rather than being imported into another program. As you are only importing the set.py program into the get.py program, the setvar() function never got called, hence the x variable never got defined.
Requirement is that I can't call the variable from setvar function.
That's not a problem! All you'll need to do is call the setvar() function outside of the if __name__ == '__main__': block once, and the problem would be fixed:
set.py:
def setvar():
global x
x = 100
def getvar():
return x
setvar()
if __name__ == '__main__':
getvar()
get.py:
import set
y = set.getvar()
print(y)
Output:
100

How can I check if the current string and previous string in Python code?

In this code I want to compare the previous message with the current message. So I created a variable to save the previous message. I wanted to create it as a static variable then manipulate it inside the code. but the outside the x function if I declare the variable it shows an error.
flag = 1
previousMessage = "abc"
def x():
do_something
currentMessage = m #got a string from code
if(currentMessage==previousMessage):
#shows error in flag and previousMessgae
#says create parameter of previousMessage and flag
flag=0
return
else:
do_something
previousNews=currentNews
flag=1
return
def call():
while True:
if(flag==1)
x()
time.sleep(60)
elsif(flag==0)
time.sleep(60) **strong text**
call()
Not sure if this is what you need. Try adding global before flag and previousMessage to make that variable a global variable.

Name is local and global syntax error in python

I am getting error while executing below statement
a = 10
def test_method(a):
global a
print("Old value of local a = " + str(a)) #this is returning "a = 10"
a = 2
print("New value of local a = " + str(a))
test_method(a)
print("Value of global a = " + str(a))
Output
SyntaxError: name 'a' is local and global
How to change value of a globally in this proc where name is same for function argument and actual parameter
I really think the correct answer is to just pick a different name for the function argument as I commented, but if you cannot for some reason, you can access the global a via globals():
>>> a = 10
>>> def test_method(a):
... print('global:', globals()['a'])
... print('local: ', a)
...
>>> test_method(42)
global: 10
local: 42
Although this is somewhat hacky and I would strongly recommend simply changing the variable names to avoid the error instead.

How can I use external variables in Python like 'extern int x;' in C?

How can I use external variables in Python, like extern int x; in C?
For example,
main1.py:
from myfunc import print_a
a = 10
print a
print_a()
myfunc.py:
def print_a():
global a
print a
Simply re-assign the variable in the module:
import myfunc
from myfunc import print_a
a = 10
print a
myfunc.a = a
print_a()
Otherwise it is not possible.
Rememeber that python treats modules in a way that is quite different from C.
The import in python does not "copy the contents" of the file in that place,
but it executes the code in the given file and creates a module object.
The global variable of the module are the module object attributes, which can be modified as I've shown. There is no such notion as "global variable" except for built-ins.
I'd suggest to refactor your code in such a way that you don't have to modify this global variable at all, moving the code that uses myfunc.a from main1 to myfunc.
The fact that you need such global variable is already a code smell that there's something wrong with your code and you should try to fix it.
Actually there is a way to affect the "global scope" but it is so hackish that I don't even want to mention it. Trust me: you don't want to use it. If people see your code using such a hack you may be in physical danger.
Unlike C, variables declared at global scope are still limited in scope to the module they are created in, so you need to qualify the name a with the module it lives in.
The global keyword is used when you are going to modify a global variable by reassigning, you do not need it when you are just referencing a global variable.
If you are trying to access a variable of another file, you must import that module, and because of the way your files are structured you have a couple of ways to resolve issues:
Option 1) Move the referencing of myfunc.print_a inside of a function and import main1 inside myfunc to see a
main1.py
import myfunc
a = 10
def main():
print a
myfunc.print_a()
if __name__ == '__main__':
main()
myfunc.py
import main1
def print_a():
print main1.a
Option 2) recommended Move the variable(s) into another module and have both myfunc and main1 import it.
vals.py
a = 20
main1.py
import vals
from myfunc import print_a
vals.a = 10
print vals.a
print_a()
myfunc.py
import vals
def print_a():
print vals.a
This is a workaround to this problem by using a common external file. In this example I am storing an index variable to flag in each application whether a file is being accessed. The variable indxOpen in ext1.py and indxO in ext2.py are being updated and stored in a common external text file "externalVars.txt"
lead application ext1.py
# lead application ext1.py
#this alternately flips the value of indxOpen on prime number intervals
import time
def update(d,v1):
f=open(d+'externalVars.txt','r+')
f.write(str( v1))
f.truncate()
f.close()
# ensure variable is initialised and made available to external apps
indxOpen = False
var_dir = "<your external var directory>/"
try:
f =open(var_dir+'externalVars.txt','r')
except:
f= open(var_dir+'externalVars.txt','w')
f.close()
# this alternately flips the value of indxOpen on prime number intervals
update(var_dir,indxOpen)
i = 0
while True:
while indxOpen:
i += 1
if (i % 13) ==0:
indxOpen = indxOpen ^ True
update(var_dir,indxOpen)
f=open(var_dir+'externalVars.txt','r+')
t=f.readline()
print "app1",t," ",i
if t=='False':
print "app1 updated"
update(var_dir,indxOpen)
indxOpen = False
else:
time.sleep(1.4)
while not indxOpen:
f=open(var_dir+"externalVars.txt","r+")
t=f.readline()
print "app1",t
if t=='True':
indxOpen = True
else:
time.sleep(1)
ext2.py following application
# ext2.py this alternately flips the value of indxO on prime number intervals but it is initialised by the lead application
# in this case ext1.py
# python 2.7.12
import time
def update(d,v1):
f=open(d+'externalVars.txt','r+')
f.write(str( v1))
f.truncate()
f.close()
var_dir = "<your external var directory>/"
# intialise external variable
f=open(var_dir+'externalVars.txt','r+')
t=f.readline()
if t=='True':
indxO= True
if t=='False':
indxO= False
i=0
while True:
while indxO:
f=open(var_dir+"externalVars.txt","r+")
t=f.readline()
print "app2",t
if t=='False':
indxO = False
update(var_dir,indxO)
else:
time.sleep(1.5)
while not indxO:
i += 1
if (i % 17) ==0:
indxO = indxO ^ True
update(var_dir,indxO)
f=open(var_dir+"externalVars.txt","r+")
t=f.readline()
print "app2",t," ",i
if t=='True':
indxO = True
print "apt2 updated"
update(var_dir,indxO)
else:
time.sleep(1.3)

how does the compile() work in python?

I have two code which really confused me.
def get_context():
__gc = globals()
__lc = locals()
def precompiler(code):
exec code in __lc
def compiler(script, scope):
return compile(script, scope, 'eval')
def executor(expr):
return eval(expr, __gc, __lc)
return precompiler, compiler, executor
maker1, compiler1, executor1 = get_context()
maker2, compiler2, executor2 = get_context()
maker1("abc = 123")
maker2("abc = 345")
expr1 = compiler1("abc == 123", "test.py")
print "executor1(abc == 123):", executor1(expr1)
print "executor2(abc == 123):", executor2(expr1)
the result is:
executor1(abc == 123): True
executor2(abc == 123): False
Why the compile execute in the closure only once and the byte-code could run in both?
And there is another code here:
def get_context():
__gc = globals()
__lc = locals()
test_var = 123
def compiler(script, scope):
return compile(script, scope, 'eval')
def executor(expr):
return eval(expr, __gc, __lc)
return compiler, executor
compiler1, executor1 = get_context()
compiler2, executor2 = get_context()
expr1 = compiler1("test_var == 123", "test.py")
print "executor1(test_var == 123):", executor1(expr1)
print "executor2(test_var == 123):", executor2(expr1)
the result is:
NameError: name 'test_var' is not defined
And how did this happen?
Why does the compile need to check the environment(variable or some others) of the closure while it is not dependent on the closure? This is what I confused!
In your first example, you are executing 'abc=123' in your first context, and 'abc=345' in your second context. So 'test_var==123' is true in your first context and false in your second context.
In your second example, you have caught an interesting situation where the interpreter has removed test_var from the context because test_var isn't referenced.
For your first question, compile just takes the python code and produces the bytecode. It it is not dependent in any way on the closure where you compiled it. Its not different then if you had produced say, a string. That string isn't permantely tied to the function where it was created and neither is the code object.
For your second question, locals() builds a dictionary of the local variables when it is called. Since you setup test_var after calling locals it doesn't have it. If you want test_var inside locals, you need to call it afterwards.

Categories