Wednesday, April 6, 2011

How to initialize variables to None/Undefined and compare to other variables in Python?

Basically I have some variables that I don't want to preinitialize:

originalTime = None
recentTime = None
postTime = None

def DoSomething ( ) :
    if originalTime == None or (postTime - recentTime).seconds > 5 :
        ...

I get compile error on the if:

UnboundLocalError: local variable 'originalTime' referenced before assignment

As you can see, all the variables have different relationship that either has to be set right (time, time + 5, etc) or None at all, but I don't wanna set them to precalculated values when just declaring them to None is easier.

Any ideas?

From stackoverflow
  • You should use an try except block to catch this.

  • I just tried your code in the shell and I didn't get an error. It should work. Maybe post the entire code? You can also use try/catch.

    Or maybe locals().has_key('originalTime') ?

  • Your code should have worked, I'm guessing that it's inside a function but originalTime is defined somewhere else. Also it's a bit better to say originalTime is None if that's what you really want or even better, not originalTime.

    Joan Venge : Thanks for the is/not tip.
    Jarret Hardie : Just to add to Scott's apposite advice on is None: keep in mind that "not originalTime" will match cases where originalTime is 0 or "" or False, whereas originalTime is None will match only those cases where originalTime is actually "None". Many developers use this difference to delineate variables that have not been specified from those that have no value in the execution context. (ie: def myfunc(somevar=None): if somevar is None: somevar = 5 # otherwise assume that somevar = 0 is deliberate)
  • If the if statement is inside a function, but the = None declarations are at the module-level, then the variables are out of scope inside the function. The simplest fix is to explicitly indicate that the variable identifiers are to be found in the global scope:

    def doSomething():
        global originalTime
        if originalTime:
            print "originalTime exists and does not evaluate to False"
    

    Many folks regard this as poor Python design, btw. If you agree with that assessment, and your architecture permits, you may wish to refactor your function so that it receives external dependencies as function arguments.

    Joan Venge : Thanks, for the bad design I agree. If it was pure python, I wouldn't do this, but I am using the python integration of a 3d party app, which is done very badly and buggy, but that's what I have to use.
  • There's not really any "pretty" way around this. Just based on the variable names that you've given, my first instinct is to create an object:

    class SomeTimeClass(object):
        def __init__(self, recentTime=None, originalTime=None, postTime=None):
            self.recentTime = recentTime
            self.originalTime = originalTime
            self.postTime = postTime
    
    time = SomeTimeClass()
    if not time.recentTime::
        ...
    

    This might work because it sounds like the variables are correlated. A couple of other options:

    Wrap the procedure in a function:

    def SomeFunc(recentTime=None, originalTime=None, postTime=None):
        if not recentTime:
            ...
    

    Use a dict:

    some_dict = {}
    
    if not some_dict.get('originalTime', None): #return None if key doesn't exist
        ...
    
    Jason Baker : Just out of curiosity, why was this voted down? Did I get something wrong?
  • I need to correct Jarret Hardie, and since I don't have enough rep to comment.

    The global scope is not an issue. Python will automatically look up variable names in enclosing scopes. The only issue is when you want to change the value. If you simply redefine the variable, Python will create a new local variable, unless you use the global keyword. So

    originalTime = None
    
    def doSomething():
      if originalTime:
        print "originalTime is not None and does not evaluate to False"
      else:
        print "originalTime is None or evaluates to False"
    
    def doSomethingElse():
      originalTime = True
    
    def doSomethingCompletelyDifferent()
      global originalTime
      originalTime = True
    
    doSomething()
    doSomethingElse()
    doSomething()
    doSomethingCompletelyDifferent()
    doSomething()
    

    Should output:

    originalTime is None or evaluates to False
    originalTime is None or evaluates to False
    originalTime is not None and does not evaluate to False
    

    I second his warning that this is bad design.

0 comments:

Post a Comment