Redefining print function not working within a function
<p>I am writing a python script in python 3.x in which I need to redefine the <code>print</code> 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.</p>

<p>Here is my code:</p>

<pre><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
&gt;&gt; 'Wow!Great!Epic!'
</code></pre>

<p>As you can see I have got my desired result: <code>'Wow!Great!Epic!'</code></p>

<p>Now I make a function using the same code:</p>

<pre><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
</code></pre>

<p>Now when I run this function using the following code:</p>

<pre><code>list = ["print('Wow!')\n", "print('Great!')\n", "print('Epic!')\n"]

execute(list)
</code></pre>

<p>I get the following error:</p>

<pre><code>old_print = print 
UnboundLocalError: local variable 'print' referenced before assignment
</code></pre>

<p>Does anyone know why this is not working within a function?<br>
Any suggestions on how to fix it will be highly appreciated.</p>

<p>All you need is <em>nonlocal</em> and to forget all the other variables you have created bar <code>catstr</code>:</p>

<pre><code>def execute(lst):
    def print(s):
        nonlocal catstr
        catstr += s
    catstr = ""
    for item in lst:
        s = item
        exec(s)
    return catstr
</code></pre>

<p>That gives you:</p>

<pre><code>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!'
</code></pre>

<p>Anything that happens in the function is <em>local to the function</em> 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 <code>old_print = __builtins__.print</code>.</p>

<p>If you want to have your function print without needing a print call use <code>__builtins__.print</code> to do the printing:</p>

<pre><code>def execute(lst):
    catstr = ""
    def print(s):
        nonlocal catstr
        catstr += s
    for s in lst:
        exec(s)
    __builtins__.print(catstr)
</code></pre>

python function printing exec