Reputation: 1865
I am writing a python script in python 3.x in which I need to redefine the print
function. When I do it in my interpreter, it works fine. But when I create a function using the same code, it gives out error.
Here is my code:
list = ["print('Wow!')\n", "print('Great!')\n", "print('Epic!')\n"]
old_print = print
def print(s):
global catstr
catstr += s
catstr = ""
for item in list:
s = item
exec(s)
print = old_print
catstr
>> 'Wow!Great!Epic!'
As you can see I have got my desired result: 'Wow!Great!Epic!'
Now I make a function using the same code:
def execute(list):
old_print = print
def print(s):
global catstr
catstr += s
catstr = ""
for item in list:
s = item
exec(s)
print = old_print
return catstr
Now when I run this function using the following code:
list = ["print('Wow!')\n", "print('Great!')\n", "print('Epic!')\n"]
execute(list)
I get the following error:
old_print = print
UnboundLocalError: local variable 'print' referenced before assignment
Does anyone know why this is not working within a function?
Any suggestions on how to fix it will be highly appreciated.
Upvotes: 1
Views: 1019
Reputation: 9863
Your issue has been already addressed by Prune and Padraic Cunningham answers, here's another alternative way to achieve (i guess) what you want:
import io
from contextlib import redirect_stdout
g_list = ["print('Wow!')\n", "print('Great!')\n", "print('Epic!')\n"]
def execute(lst):
with io.StringIO() as buf, redirect_stdout(buf):
[exec(item) for item in lst]
return buf.getvalue()
def execute_modified(lst):
result = []
for item in lst:
with io.StringIO() as buf, redirect_stdout(buf):
exec(item)
result.append(buf.getvalue()[:-1])
return "".join(result)
print(execute(g_list))
print('-' * 80)
print(execute_modified(g_list))
Output:
Wow!
Great!
Epic!
--------------------------------------------------------------------------------
Wow!Great!Epic!
Upvotes: 1
Reputation: 180481
All you need is nonlocal and to forget all the other variables you have created bar catstr
:
def execute(lst):
def print(s):
nonlocal catstr
catstr += s
catstr = ""
for item in lst:
s = item
exec(s)
return catstr
That gives you:
In [1]: paste
def execute(lst):
def print(s):
nonlocal catstr
catstr += s
catstr = ""
for item in lst:
s = item
exec(s)
return catstr
## -- End pasted text --
In [2]: list = ["print('Wow!')\n", "print('Great!')\n", "print('Epic!')\n"]
In [3]: execute(lst)
Out[3]: 'Wow!Great!Epic!'
Anything that happens in the function is local to the function so you don't need to worry about resetting anything. If you did happen to want to set a reference to print you could use old_print = __builtins__.print
.
If you want to have your function print without needing a print call use __builtins__.print
to do the printing:
def execute(lst):
catstr = ""
def print(s):
nonlocal catstr
catstr += s
for s in lst:
exec(s)
__builtins__.print(catstr)
Upvotes: 2
Reputation: 77850
The interpreter doesn't recognize print as the built-in function unless you specifically tell it so. Instead of declaring it global, just remove it (thanks to Padraic Cunningham): the local print will take on your desired definition, and the global is never affected.
You also have a forward-reference problem with catstr. The below code elicits the desired output.
catstr = ""
def execute(list):
def print(s):
global catstr
catstr += s
for item in list:
s = item
exec(s)
print = old_print
return catstr
list = ["print('Wow!')\n", "print('Great!')\n", "print('Epic!')\n"]
print (execute(list))
Upvotes: 2