I l@ve RuBoard Previous Section Next Section

17.2 Testing if a Variable Is Defined

Credit: Hamish Lawson

17.2.1 Problem

You want to take different courses of action based on whether a variable is defined.

17.2.2 Solution

In Python, all variables are expected to be defined before use. The None object is a value you often assign to signify that you have no real value for a variable, as in:

try: x
except NameError: x = None

Then it's easy to test whether a variable is bound to None:

if x is None:
    some_fallback_operation(  )

17.2.3 Discussion

Python doesn't have a specific function to test whether a variable is defined, since all variables are expected to have been defined before use, even if initially assigned the None object. Attempting to access a variable that hasn't previously been defined raises a NameError exception (which you can handle with a try/except statement, as you can for any other Python exception).

It is considered unusual in Python not to know whether a variable has already been defined. But if you are nevertheless in this situation, you can make sure that a given variable is in fact defined (as None, if nothing else) by attempting to access it inside a try clause and assigning it the None object if the access raises a NameError exception. Note that None is really nothing magical, just a built-in object used by convention (and returned by functions that exit without returning anything specific). You can use any other value suitable for your purposes to initialize undefined variables; for a powerful and interesting example, see Recipe 5.24.

Instead of ensuring that a variable is initialized, you may prefer to test whether it's defined where you want to use it:

try: x
except NameError: some_fallback_operation(  )
else: some_operation(x)

This is a perfectly acceptable alternative to the code in the recipe, and some would say it's more Pythonic. Note, however, that if you choose this alternative, you have to code things in this order: the anomalous, error case first, then the normal, no-error case. With the recipe's approach, you may want to invert the guard condition to if x is not None and code the normal case first. These points are minutiae, to be sure, but sometimes clarity can be improved this way. Furthermore, you must be careful to avoid the variation in this alternative:

except NameError:
    some_fallback_operation(  )

In this variation, the call to some_operation is also covered by the exception handler, so if there is a bug in the some_operation function, or in any function called from it, this code would mask the bug and apparently proceed to operate normally when it should fail with an error message. You should always be careful that your try clauses (in try/except statements) do not accidentally cover more code than you actually intend to cover, which might easily mask bugs. The else clause in the try/except statement is for code that should execute only if no exception was raised but should not itself be covered by the exception handler, because you do not expect exceptions from it and want to diagnose the problem immediately if exceptions do occur.

Many situations that you might think would naturally give rise to undefined variables, such as processing configuration files or web forms, are handled better by employing a dictionary and testing for the presence of a key (with the has_key method, a try/except, or the get or setdefault methods of dictionary objects). For example, instead of dealing with a user configuration file this way:

try: background_color
except NameError: background_color = 'black'
try: foreground_color
except NameError: foreground_color = 'white'

do it this way:

config = dict(globals(  ))
execfile('userconfig', config)
background_color = config.get('background_color', 'black')
foreground_color = config.get('foreground_color', 'white')

dict requires Python 2.2, but you can get a similar effect in earlier versions of Python by using config = globals().copy( ) instead. Using an explicitly specified dictionary for exec, eval, and execfile is advisable anyway, to keep your namespace under control. One of the many benefits of using such an explicitly specified dictionary is, as shown here, that you don't need to worry about undefined variables but can simply use the dictionary's get method to fetch each key with an explicitly specified default value to be used if the key is not present in the dictionary.

If you know for sure which namespace the variable is in (i.e., specifically locals or specifically globals), you can also use methods such as has_key or get on the relevant dictionary. However, variables that are in neither locals nor globals may exist (thanks to the nested scopes feature that is optional in Python 2.1, but is always on in Python 2.2 and later). Also, the special namespace directories returned by locals and globals are not suitable for mutating methods such as setdefault, so you're still better off arranging to use your own explicit dictionary rather than the local or global namespaces, whenever that's feasible.

17.2.4 See Also

Recipe 5.24.

    I l@ve RuBoard Previous Section Next Section