Check if a Variable Exists in Python (Without Getting It Wrong)

To check if a variable exists in Python, the idiomatic answer is to use it and catch the failure:

try:
    print(count)
except NameError:
    print("count does not exist")

"count" in globals()     # the membership test, with a catch

The membership test looks tidier, and it has a trap. Inside a function, "x" in locals() returns False for a module-level variable that’s perfectly valid.

The scope behaviour below is real output from Python 3.12.5.

Checking whether a variable exists

Three namespaces can be inspected, and at module level they give the same answer:

count = 10

# the three ways to ask, at module level
print("'count' in dir()     :", "count" in dir())
print("'count' in globals() :", "count" in globals())
print("'count' in locals()  :", "count" in locals())

print()
print("at module level they agree, because locals() IS globals():")
print("  locals() is globals() ->", locals() is globals())

Output:

'count' in dir()     : True
'count' in globals() : True
'count' in locals()  : True

at module level they agree, because locals() IS globals():
  locals() is globals() -> True
Command Prompt showing dir, globals and locals all confirming a Python variable exists at module level
At module level, locals() and globals() are the same object.
CheckLooks inScope
"x" in dir()The current namespaceWherever you call it
"x" in locals()Local names onlyFunction-local inside a function
"x" in globals()Module-level namesThe whole module
try / except NameErrorEvery scope Python wouldAlways correct

That last row is the important one. Only the exception approach follows the same lookup rules Python itself uses.

Why does ‘x’ in locals() fail inside a function?

This is where the membership test quietly breaks, and it’s the reason to avoid it:

module_level = "I am global"

def check_inside():
    function_local = "I am local"

    print("  'function_local' in locals() :", "function_local" in locals())
    print("  'function_local' in globals():", "function_local" in globals())
    print()
    print("  'module_level' in locals()   :", "module_level" in locals(), " <- surprising")
    print("  'module_level' in globals()  :", "module_level" in globals())


print("inside a function:")
check_inside()

print()
print("a module-level name is NOT in a function's locals(),")
print("so 'name' in locals() gives the wrong answer there")

Output:

inside a function:
  'function_local' in locals() : True
  'function_local' in globals(): False

  'module_level' in locals()   : False  <- surprising
  'module_level' in globals()  : True

a module-level name is NOT in a function's locals(),
so 'name' in locals() gives the wrong answer there
Command Prompt showing that a module level variable is not present in a function's locals but is in globals
module_level is missing from locals() and present in globals().

Python looks names up through local, enclosing, global and built-in scopes in turn. locals() only shows you the first of those four.

So a function that checks "config" in locals() reports False for a global it can read perfectly well on the next line.

Checking globals() instead has the mirror problem: it misses local names, and it misses anything in an enclosing function.

Using try and except NameError in Python

Catching the error covers every scope, because Python does the lookup for you:

count = 10

# the idiomatic Python way: try it and handle the failure
try:
    print("count is", count)
except NameError:
    print("count does not exist")

print()
try:
    print(missing_name)
except NameError as err:
    print("missing_name ->", type(err).__name__ + ":", err)

print()
print("this works at any scope, and it is what Python programmers expect to read")

Output:

count is 10

missing_name -> NameError: name 'missing_name' is not defined

this works at any scope, and it is what Python programmers expect to read
Command Prompt showing a try except NameError block used to test whether a Python variable exists
The name resolves or it raises. No namespace guessing.

This is the style Python programmers call EAFP, easier to ask forgiveness than permission. It is the convention throughout the standard library.

The error it catches is the same one covered in fixing NameError, where the usual causes are listed.

Which variable existence check is faster?

The idiomatic choice turns out to be the quick one as well:

import timeit

setup = "v = 1"

membership = timeit.timeit("'v' in globals()", setup=setup, number=1_000_000)
exception = timeit.timeit("try:\n v\nexcept NameError:\n pass", setup=setup, number=1_000_000)

print(f"'v' in globals()  {membership * 1000:>7.0f} ms")
print(f"try / except      {exception * 1000:>7.0f} ms")

print()
print("try/except is not only idiomatic, it is the faster of the two")
print("a successful lookup costs nothing extra, because no exception is raised")

Output:

'v' in globals()       26 ms
try / except           11 ms

try/except is not only idiomatic, it is the faster of the two
a successful lookup costs nothing extra, because no exception is raised

A successful try block costs almost nothing. Python sets up the handler and never uses it, so there’s no exception to build.

The membership test, by contrast, builds a dictionary view and hashes your string on every call.

Exceptions are only expensive when they’re actually raised. If the variable usually exists, try wins on both style and speed.

Avoiding the check altogether

Most of the time the real fix is to make sure the name always exists:

# usually you do not need to check at all: give the name a value first
result = None

for value in []:               # this loop never runs
    result = value

if result is None:
    print("nothing was found")
else:
    print("found:", result)

print()
# and for optional settings, a default is clearer than a check
config = {"host": "localhost"}
port = config.get("port", 8080)
print("port:", port, " (from the default)")

Output:

nothing was found

port: 8080  (from the default)

Assigning result = None before a loop guarantees the name is there even when the loop body never runs.

That turns “does this exist” into “is this still None”, which is a much easier question and needs no namespace inspection.

If you find yourself checking whether a variable exists, it’s usually a sign that a code path forgot to set it. A default fixes the cause rather than the symptom.

Checking attributes and dictionary keys

Two neighbouring questions get confused with this one, and each has its own tool:

class Settings:
    debug = True


s = Settings()

# an ATTRIBUTE on an object
print("hasattr(s, 'debug')   :", hasattr(s, "debug"))
print("hasattr(s, 'verbose') :", hasattr(s, "verbose"))
print("getattr with default  :", getattr(s, "verbose", False))

print()
# a KEY in a dictionary
data = {"name": "Ana"}
print("'name' in data        :", "name" in data)
print("data.get('age')       :", data.get("age"))
print("data.get('age', 0)    :", data.get("age", 0))

print()
# these are different questions from "does this variable exist"
print("a NameError means the name itself is unknown")
print("a KeyError or AttributeError means the container lacks it")

Output:

hasattr(s, 'debug')   : True
hasattr(s, 'verbose') : False
getattr with default  : False

'name' in data        : True
data.get('age')       : None
data.get('age', 0)    : 0

a NameError means the name itself is unknown
a KeyError or AttributeError means the container lacks it
Command Prompt showing hasattr and dict get used to check for an attribute and a dictionary key in Python
hasattr for objects, in or .get() for dictionaries.
  • hasattr(obj, "name") — does this object have that attribute?
  • getattr(obj, "name", default) — fetch it, with a fallback.
  • "key" in mapping — does this dictionary have that key?
  • mapping.get("key", default) — fetch it, with a fallback.

A NameError means Python cannot find the name at all. A KeyError or AttributeError means the name resolved fine and the container did not have what you asked for. See checking a dictionary for the mapping case.

Common mistakes checking if a variable exists

SymptomCauseFix
False for a working globalUsed locals() in a functionUse try / except
False for a localUsed globals()Use try / except
NameError on the check itselfPassed the variable, not its nameQuote it: "x" in dir()
Works, but reads awkwardlyChecking instead of defaultingAssign None up front
KeyError, not NameErrorIt is a dictionary keyUse .get()

Other Python variable and scope guides:

Frequently asked questions

How do I check if a variable exists in Python?

Use it inside a try block and catch NameError. The exception is described in the Python built-in exceptions reference.

What is the difference between locals() and globals()?

locals() holds names in the current scope, globals() holds module-level names. At module level they are the same object; inside a function they are not.

Why does ‘x’ in locals() return False inside a function?

Because module-level variables are not local to the function. Python finds them through the global scope, which locals() does not include.

Is try/except slower than checking globals()?

No. When the variable exists, try is faster, because no exception is ever created.

How do I check if an object has an attribute?

hasattr(obj, "name"), or getattr(obj, "name", default) to fetch it with a fallback.

How do I check if a dictionary key exists?

"key" in mapping, or mapping.get("key", default). That raises KeyError, not NameError.

Should I check if a variable exists at all?

Usually not. Assign a default such as None before any branch that might skip it, then test the value instead of the name.