1

I mean to define a function print_echo that replaces print, such that in addition to printing the result of an expression it prints the expression itself.

If I simply pass the expression as a string and use eval inside print_echo, it will not know any variable local to the caller function. My current code is

def print_echo( expr ) :
    result = eval( expr )
    print( expr + ' => ' + str( result ) + ' ' + str( type( result ) ) )
    return

But when using

def my_func( params ) :
    a = 2
    print_echo( "a" )

I get (no surprise)

NameError: name 'a' is not defined

I mean to get

    a => 2 <type 'int'>

I conceived two ways of working around this.

  1. Use a Python like alternative for C preprocessor macros. Something like C Preprocessor Macro equivalent for Python

  2. Pass all local variables to print_echo. Something like Passing all arguments of a function to another function

Since I find inconvenient aspects for each of the two, Is there any alternative to these?

Note that expr is a generic expression, not necessarily the name of a variable.

2 Answers2

2

eval() only takes the global namespace, and the namespace local to where it is called, into account.

In your case, you need the namespace of where print_echo is called (i.e., the "parent" namespace of where eval is called) as the local namespace, which you can get by using the inspect module and pass to eval as an argument.

import inspect

def print_echo(expr):
    outer_locals = inspect.currentframe().f_back.f_locals
    result = eval(expr, globals(), outer_locals)
    print(expr, '=>', result, type(result))

a = 2
print_echo('a')

def f():
    b = 3
    print_echo('b')

f()

Python 3 output:

a => 2 <class 'int'>
b => 3 <class 'int'>
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
  • It would be better to use the outer frame's globals (`f_globals`) and not those of `print_echo` (which just so happen to be the same in your example). – Dunes Oct 25 '18 at 13:14
  • @Dunes You mean replacing `globals()` by `f_back.f_globals`? – mkrieger1 Oct 25 '18 at 13:53
0

Important Note: There can be more error handling to this case. For more info, you can see inspect and explore it further. https://docs.python.org/2/library/inspect.html

import inspect

# NOTE: this only prints the local variables to the function
def print_echo( *expr ) :

    frame = inspect.currentframe().f_back # see the previous frame and what all variable it's previous caller knows
    values = inspect.getargvalues(frame)[3]
    print values # just to understand what it is, remove it later
    for e in expr:
        try:
            result = values[e]
        except KeyError:
            eval(e) # see the globally defined variables, if not found local to previous function.
        print( str(e) + ' => ' + str( result ) + ' ' + str( type( result ) ) )
Sujay Kumar
  • 520
  • 8
  • 19