views:

96

answers:

5

Hello,

I'm apparently laboring under a poor understanding of Python scoping. Perhaps you can help.

Background:
I'm using the

if __name__ == "__main__"

construct to perform "self-tests" in my module(s). Each self test makes calls to the various public methods and prints their results for visual checking as I develop the modules.

To keep things "purdy" and manageable, I've created a small method to simplify the testing of method calls:

def pprint_vars(var_in):
    print("%s = '%s'" % (var_in, eval(var_in)))

When

foo = "bar"

Calling pprint_vars with:

pprint_vars('foo')

prints:

foo = 'bar'

All fine and good.

Problem statement:
Not happy to just KISS, I had the brain-drizzle to move my handy-dandy 'pprint_vars' method into a separate file named 'debug_tools.py' and simply import 'debug_tools' whenever I wanted access to 'pprint_vars'.

Here's where things fall apart. I would expect

import debug_tools

foo = bar
debug_tools.pprint_vars('foo')

to continue working its magic and print:

foo = 'bar'

Instead, it greets me with:

NameError: name 'some_var' is not defined

Irrational belief:
I believed (apparently mistakenly) that import puts imported methods (more or less) "inline" with the code, and thus the variable scoping rules would remain similar to if the method were defined inline.

Plea for help:
Can someone please correct my (mis)understanding of scoping regards imports?

Thanks, JS

+1  A: 

"Global scope" doesn't actually exist in Python. What is commonly called "global scope" is actually module scope. That is to say, names defined at the module level. Putting the function in another module means that its module scope changes.

Ignacio Vazquez-Abrams
What would you suggest as the preferred way to accomplish my goal?
JS
Use the various `inspect.*frame*()` functions to get the caller's frame, then access the `f_locals` attribute of the frame to get the locals: `inspect.getouterframes(inspect.currentframe())[1][0].f_locals[var_in]`
Ignacio Vazquez-Abrams
A: 

eval accepts two optional parameters -- global and local -- which you can specify as the context to evaluate your source:

print("%s = '%s'" % (var_in, eval(var_in, globals())))
Satoru.Logic
This won't help if `var_in` is supposed to be in a completely different, unknown module.
Ignacio Vazquez-Abrams
+1  A: 

In python each file is its own namespace. A function, when called, resolves its variables in the following order:

  1. local variables (that includes captured variables or in a closure)
  2. (module) global variables
  3. builtins

You can use the (interpreter specific) inspect module to navigate the callstack as a list of frames. Since a frame knows its locals, globals, builtins and parent frame, you can 'see' the interpreter through the eyes of your caller like this (please only use for debugging):

import inspect
def log_var(name):
    f = inspect.currentframe().f_back
    if name in f.f_locals:
        print "local `%s` = %r" % (name, f.f_locals[name])
    elif name in f.f_globals:
        print "global `%s` = %r" % (name, f.f_globals[name])
    elif name in f.f_builtins:
        print "builtin `%s` = %r" % (name, f.f_builtins[name])
    else:
        print "`%s` not found" % name
Bendlas
A: 

I would recommend passing the locals() to the function, like so...

def pprint_var(var_name, D):
    print "%s = %r" % (var_name, D[var_name])

...

pprint_var(var_name, locals())

if you don't want that, you can use the inspect module or sys._getframe to do the same thing, as other have suggested.

magcius
A: 

The basic issue I'm running into is that Python has no sense of name for simple objects. For methods, classes, etc. yes, for simple objects, no.

So in languages that feature variables, my 'ppvar' function would look something like this (pseudo-code):

ppvar( var_name ):
    print("%s = '%s'", var_name, $var_name)

I could import the file containing 'ppvar' in any project and call it and it would essentially be "inlined" into my code.

Alas this is most likely a "functional programming" concept and not suitable for object-oriented approaches.

All languages have the pluses and minuses. Every language has a few "simple" things it falls down on. Being able to peform something like:

print("%s = '%s'" % (var.__name__, var)

in Python is (as of yet) not something that will work. Oh well.

JS