1

I have a function and a flask route setup. However, when I go to "simplepage", I get an error "NameError: global name 'aaa' is not defined". Why aren't any of the objects being passed to testfun? Is this something due to the app.route decorator, or due to flask? Can I make all the objects passed to testfun? My actual code is way more complicated with many more objects that would need to be passed, but this simplified scenario was created to illustrate my issue.

def testfun():
    b=aaa

@app.route("/simplepage/", methods=['GET'])
def simplepage():
    aaa=1
    testfun()
    return flask.render_template('page.html')
user1748155
  • 1,345
  • 1
  • 9
  • 17
  • Why not pass `aaa` as an argument to `testfun()` (i.e. `testfun(aaa)`) – John Apr 17 '13 at 02:21
  • well, in my real application there are a lot more objects to pass and i would also have to re-import a lot of modules too, and do this for multiple functions. i'd prefer to avoid all that and make the code cleaner and simpler to maintain. i don't think i normally have this problem. – user1748155 Apr 17 '13 at 02:25
  • After a little research I'm fairly sure this is a [scoping](http://stackoverflow.com/a/292502/322909) issue... – John Apr 17 '13 at 02:32

2 Answers2

3

This is due to Python's scoping rules (as @johnthexiii pointed out) - testfun->aaa is bound to the global scope because there is no variable named aaa declared anywhere inside of testfun and there is no enclosing scope (i. e. testfun is not declared inside another function or class).

You'll want to pass aaa as an argument to testfun:

testfun(aaa)

If testfun requires too many params there are several ways you can DRY up the code:

  • Use multiple functions: If testfun is doing a lot of work then break it up into multiple functions that return intermediate transformations of the data:

    def start_test(arg1, arg2, arg3):
        # do stuff with args
        return result_stage_1
    
    def continue_test(arg3, arg4, arg5):
        # do stuff with args
        return result_stage_2
    
    def finalize_test(arg7, arg8):
        # do stuff with args
        return real_result
    
  • Use keyword arguments: If a variable number of arguments are needed and testfun cannot be broken up you can use keyword arguments to simplify the calling of the function:

    def testfun(arg1=None, arg2=None, arg3=None, arg4=None,
                  arg5=None, arg6=None, arg7=None, arg8=None):
        # do stuff with args
    
    # call it as so
    args = {"arg1": "some args", "arg5": "have defaults"}
    if complex_condition:
        args["arg3"] = 17
    elif another_condition:
        args["arg7"] = 10
    
    testfun(**args)
    
  • Encapsulate state and behavior with classes (or closures): If neither of the above will work for you then you probably need to leave the realm of stateless functions and create classes or closure-creating functions to preserve your state and enable you to modify behavior as needed.

Community
  • 1
  • 1
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
1

Basically what is happening is this

def t():
    print aaa 

def s():
    aaa = "hi"
    t()

s()

Python looks for aaa first in the local scope, then in any wrapping function scopes, then globally, and lastly in the builtins. Since the s functions scope isn't any of those things python throws an undefined error because it can't find aaa.

One solution would be to declare aaa as global.

def t():
    print aaa

def s():
    global aaa
    aaa = "hi"
    t()

s()
John
  • 13,197
  • 7
  • 51
  • 101
  • I guess the confusion is that an object has to be defined where the most top level function is called or defined, but any sub functions only get objects passed to them from the level where they have been defined and not where they are called. – user1748155 Apr 17 '13 at 14:47
  • def s(): def t(): print aaa aaa = "hi" t() s() – user1748155 Apr 17 '13 at 14:49