Skip to main content

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.

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
tip

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.

caution

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

  1. inspect.currentframe() gets the current function's frame.
  2. frame.f_back accesses the caller's frame.
  3. frame.f_back.f_locals provides the caller's local variables.
  4. We search this dictionary for the matching object.
info

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

MethodScopeRequires ImportPython VersionReliable
f"{var=}"Current expressionNo3.8+✅ Most reliable
globals()Global variablesNoAll⚠️ May find multiple matches
locals()Current local scopeNoAll⚠️ Shows local names only
inspect moduleCaller's scopeYesAll✅ Cross-scope lookup
Custom namespaceAny provided scopeNoAll⚠️ 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() or locals() to search namespace dictionaries when you need programmatic access to variable names.
  • Use the inspect module 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.