How to Print a Variable's Name in Python
When debugging, logging, or building developer tools, you may want to display the name of a variable alongside its value. For instance, instead of printing just 42, you might want to see x = 42 to understand which variable holds that value. Python doesn't provide a direct built-in way to retrieve a variable's name, but several clever techniques make it possible.
In this guide, you'll learn multiple approaches to print a variable's name in Python, understand how each one works, and know which method to choose based on your use case.
Why Python Doesn't Have a Built-In Solution
In Python, variables are simply names that reference objects. Multiple names can point to the same object, and an object has no inherent knowledge of which names reference it:
x = 42
y = x # Both 'x' and 'y' reference the same integer object 42
Since the integer 42 doesn't know whether it's called x or y, there's no universal way to ask an object for "its" variable name. All the methods below work by searching through namespaces (dictionaries of name-to-object mappings) to find which name points to a given object.
Using f-strings with = (Python 3.8+, Recommended)
Starting with Python 3.8, f-strings support the = specifier, which prints both the expression and its value. This is the simplest and most Pythonic approach:
x = 42
name = "Alice"
numbers = [1, 2, 3]
print(f"{x = }")
print(f"{name = }")
print(f"{numbers = }")
Output:
x = 42
name = 'Alice'
numbers = [1, 2, 3]
You can also use it with expressions:
a = 10
b = 20
numbers = [1, 2, 3]
print(f"{a + b = }")
print(f"{len(numbers) = }")
Output:
a + b = 30
len(numbers) = 3
The f"{var=}" syntax is the recommended approach for debugging output in Python 3.8+. It's concise, readable, and doesn't require any imports or namespace inspection.
Using globals() to Search Global Variables
The globals() function returns a dictionary of all variables in the global scope. You can search this dictionary to find which name references a specific object:
def print_var_name(var):
for name, value in globals().items():
if value is var:
print(f"Variable name: {name}")
return
my_number = 100
print_var_name(my_number)
Output:
Variable name: my_number
How It Works
globals() returns a dictionary like {'my_number': 100, '__name__': '__main__', ...}. The function iterates through this dictionary and uses the is operator to check which name points to the exact same object.
The is operator checks object identity, not equality. For small integers (roughly -5 to 256), Python caches and reuses the same objects, which can lead to unexpected matches:
a = 42
b = 42 # Python reuses the cached integer object for 42
def print_var_name(var):
names = [name for name, value in globals().items() if value is var]
print(f"Matching names: {names}")
print_var_name(a)
Output:
Matching names: ['a', 'b']
Both a and b match because they reference the same cached object. This is a fundamental limitation of namespace-based approaches.
Using locals() to Search Local Variables
The locals() function returns a dictionary of variables in the current local scope. However, when used inside a function, it shows the function's own local variables: not the caller's:
def print_var_name(var):
# This searches the function's own locals, not the caller's
for name, value in locals().items():
if value is var:
print(f"Variable name: {name}")
return
x = 42
print_var_name(x)
Output:
Variable name: var
Notice that it finds var (the parameter name), not x (the caller's variable name). This is because locals() inside the function only sees the function's own scope.
Using the inspect Module for Cross-Scope Lookup
The inspect module allows you to examine the calling frame, giving access to the caller's local variables. This is the most robust method for finding a variable's name across different scopes:
import inspect
def print_var_name(var):
frame = inspect.currentframe()
try:
caller_locals = frame.f_back.f_locals
names = [name for name, value in caller_locals.items() if value is var]
if names:
print(f"Variable name: {names[0]}")
else:
print("Variable name not found in caller's scope")
finally:
del frame # Avoid reference cycles
my_data = [1, 2, 3]
print_var_name(my_data)
Output:
Variable name: my_data
How It Works
inspect.currentframe()gets the current function's frame.frame.f_backaccesses the caller's frame.frame.f_back.f_localsprovides the caller's local variables.- We search this dictionary for the matching object.
Always delete the frame reference (del frame) in a finally block to avoid reference cycles that can delay garbage collection. This is a best practice recommended in the Python documentation.
Using a Custom Namespace Dictionary
You can pass any namespace dictionary explicitly, giving you full control over which scope to search:
def print_var_name(var, namespace):
names = [name for name, value in namespace.items() if value is var]
if names:
print(f"Variable name: {names[0]}")
else:
print("Variable not found in the provided namespace")
my_list = [10, 20, 30]
print_var_name(my_list, locals())
Output:
Variable name: my_list
This approach is flexible because you can pass locals(), globals(), or even vars(some_object) as the namespace.
Practical Example: Debug Logging Helper
Here's a practical utility function that prints multiple variables with their names and values:
import inspect
def debug(*args):
"""Print variable names and values for debugging."""
frame = inspect.currentframe()
try:
caller_locals = frame.f_back.f_locals
for var in args:
names = [n for n, v in caller_locals.items() if v is var]
name = names[0] if names else "unknown"
print(f" {name} = {var!r} (type: {type(var).__name__})")
finally:
del frame
# Usage
username = "alice"
score = 95.5
items = ["sword", "shield"]
print("Debug info:")
debug(username, score, items)
Output:
Debug info:
username = 'alice' (type: str)
score = 95.5 (type: float)
items = ['sword', 'shield'] (type: list)
Common Mistake: Expecting Unique Results with Immutable Objects
A frequent issue is getting unexpected matches when multiple variables reference the same immutable object:
a = "hello"
b = "hello" # Python interns short strings, so a and b share the same object
def find_name(var):
names = [n for n, v in globals().items() if v is var]
print(f"All matching names: {names}")
find_name(a)
Output:
All matching names: ['a', 'b']
Both a and b match because Python reuses the same string object. This is inherent to how Python manages immutable objects and cannot be avoided with namespace-based approaches. The f-string = syntax doesn't have this problem because it operates on the source expression directly.
Comparison of Methods
| Method | Scope | Requires Import | Python Version | Reliable |
|---|---|---|---|---|
f"{var=}" | Current expression | No | 3.8+ | ✅ Most reliable |
globals() | Global variables | No | All | ⚠️ May find multiple matches |
locals() | Current local scope | No | All | ⚠️ Shows local names only |
inspect module | Caller's scope | Yes | All | ✅ Cross-scope lookup |
| Custom namespace | Any provided scope | No | All | ⚠️ Depends on namespace |
Summary
Python doesn't have a built-in function to retrieve a variable's name because variables are just references to objects. However, several techniques work well in practice:
- Use
f"{var=}"(Python 3.8+) for the simplest, most reliable debugging output: it's the recommended approach. - Use
globals()orlocals()to search namespace dictionaries when you need programmatic access to variable names. - Use the
inspectmodule for the most robust cross-scope lookup, especially when building debugging utilities. - Be aware that immutable object caching (integers, strings) can cause multiple variables to match the same object, making namespace-based approaches inherently imprecise.
For everyday debugging, f"{var=}" is almost always the best choice.