Elisha512 Elisha512 - 2 months ago 9
Python Question

Redefining print function in python 3 not working within a function

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 will be highly appreciated.

Answer

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)