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
locals() and globals() are the same object.| Check | Looks in | Scope |
|---|---|---|
"x" in dir() | The current namespace | Wherever you call it |
"x" in locals() | Local names only | Function-local inside a function |
"x" in globals() | Module-level names | The whole module |
try / except NameError | Every scope Python would | Always 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
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
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
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
| Symptom | Cause | Fix |
|---|---|---|
False for a working global | Used locals() in a function | Use try / except |
False for a local | Used globals() | Use try / except |
NameError on the check itself | Passed the variable, not its name | Quote it: "x" in dir() |
| Works, but reads awkwardly | Checking instead of defaulting | Assign None up front |
KeyError, not NameError | It is a dictionary key | Use .get() |
Other Python variable and scope guides:
- Fixing NameError in Python
- Python variables explained
- Check if a variable is null or empty
- Check if a variable is a boolean
- Check if a variable is an integer
- Check if an array index exists
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.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn