views:

92

answers:

3

I have a __main__ function where I initialize a lot of variables that are to be used in my program, later on. I have a problem where a variable that I temporarely declare as None in the outer scope, is assigned an object of SomeClass, but due to scoping rules I cannot access it's content in the outer scope. Because the constructor of SomeClass demands an argument to be passed, I cannot simply declare myObject to be foo.bar.SomeClass to begin with. So what must I do in order to get access to the attributes in foo.bar.SomeClass?

Codewise it looks like this:

myObject = None

def setUp():
    ... lots of initialization ...
    myObject = foo.bar.SomeClass(init_variable)

if __name__ == "__main__":
    setUp()
    myObject.member1 #throws AttributeError: 'NoneType' object has no attribute member1
+2  A: 

Assuming that setup() is meant to be initialize(), the problem is that the variable myObject in initialize() is a local variable that hides the global myObject, and when initialize() returns, the local name will go out of scope.

To update the global myObject variable, you need to change initialize() as follows:

def initialize():
    global myObject
    ... lots of initialization ...
    myObject = foo.bar.SomeClass(init_variable)

Adding the global statement means that the global myObject gets updated, rather than a local one.

Simon Callan
blimey, I wasn't aware of that! the fact that I can refer to a variable outside of the function (that I am not assigning to) lulled me into this false sense of knowing what I was doing :)
A: 

If your SomeClass requires an argument from the execution of the script, it would probably be easiest to just pass in whatever information you need to construct an instance of it as an argument. It might look something like:

def initialize(args):
    ... lots of initialization ...
    myObject = foo.bar.SomeClass(args[0])

if __name__ == "__main__":
    initialize(sys.argv)

Another way to do it would be to simply make the myObject instance global using the global keyword before calling initialize().

Mason Tang
+2  A: 

You don't have to do it that - using globals is considered bad practice for most uses. You can always return the data you want:

def initialize():
    ... lots of initialization ...
    return foo.bar.SomeClass(init_variable)

if __name__ == "__main__":
    myObject = initialize()
    myObject.member1 # works

EDIT: If you need to initialize multiple values, or if you see yourself using too much globals to share state between many toplevel functions, then it is time to use a class.

class MyProgram(object):
    def __init__(self):
        # ... lots of initialization ...
        self.myObject = foo.bar.SomeClass(init_variable)

    def usage(self):
        self.myObject.member1

if __name__ == "__main__":
    m = MyProgram()
    m.usage()
nosklo
the setup() procedure basically initalizes something like twenty variables. just found it cleaner to move this init-chunk into a method. how would you propose I do that without returning a list of twenty objects?
@oligofren: oh, if you initialize this large ammount of variables, then perhaps it's time to refactor your code into a class, use a class method to initialize and store the data as instance attributes, which are accessible on the other methods. That's what classes are **for**. I've edited my answer.
nosklo
Thanks, great answer!