Skip to main content
Python beginner Lesson 2 of 28

Python Variables and Type System

Understand variables, dynamic typing, scoping, and reference semantics in Python.

Variables in Python are references to objects in memory. Unlike languages like C++ or Java, Python does not require explicit type declarations. The type of a variable is determined at runtime based on the object it references.

Variables as References

Understanding that Python variables are references — not containers — prevents a whole class of bugs, especially when working with mutable objects like lists and dicts. When two variables point to the same object, a mutation through one is visible through the other.

# Create an integer object in memory and point 'x' to it
x = 42

# Point 'y' to the same object as 'x' — no copy is made
y = x

# id() returns the memory address of the object — both are the same
print(id(x) == id(y))  # True: x and y reference the same object

In Python, numbers and strings are immutable. When you reassign a variable, Python creates a new object rather than modifying the existing one. This is why reassigning x doesn’t affect y:

# Creates a new integer object (43) and points x to it
x = x + 1
print(x)  # 43 — x now references a new object
print(y)  # 42 — y still references the original object

Naming Conventions (PEP 8)

Consistent naming is one of the easiest ways to make code readable for your teammates — and your future self. Python’s PEP 8 style guide is the widely accepted standard across the ecosystem.

  • Use snake_case for variable and function names.
  • Use CAPITAL_SNAKE_CASE for global constants.
  • Avoid using Python keywords like list, str, or class as variable names — they shadow the built-in types and cause confusing errors.
# Good — snake_case for variables, SCREAMING_SNAKE_CASE for constants
user_name = "Alice"
MAX_RETRY_COUNT = 3

# Bad — shadows the built-in list type, causes subtle bugs
list = [1, 2, 3]

The Scope Resolution (LEGB)

Python resolves variable names by searching four scopes in order. Understanding LEGB explains why a variable defined inside a function isn’t visible outside it, and why you need global or nonlocal to modify outer variables from inner functions.

  1. Local (L): Names assigned inside a function body.
  2. Enclosing (E): Names in the local scope of enclosing functions (nonlocal).
  3. Global (G): Names assigned at the top-level of a module file.
  4. Built-in (B): Names preloaded into the built-in namespace (e.g., len, range).
# Global variable — visible everywhere in this module
prefix = "User: "

def greet(name):
    # Local variable — only accessible inside greet()
    message = f"Hello, {name}"

    def format_output():
        # Enclosing scope: format_output() can read 'message' and 'prefix'
        # even though they weren't defined here
        return f"{prefix}{message}"

    return format_output()

print(greet("Alice"))  # "User: Hello, Alice"

If you need to assign to a variable in an outer scope, use the global or nonlocal keyword to tell Python which scope you mean:

count = 0

def increment():
    global count  # without this, Python would create a new local 'count'
    count += 1

increment()
print(count)  # 1

Frequently Asked Questions

Is Python statically or dynamically typed?
Python is dynamically typed. This means variables do not have fixed types; they are references to typed objects in memory.
What is variable scoping in Python?
Variable scoping determines the accessibility of variables. Python resolves names using the LEGB rule: Local, Enclosing, Global, and Built-in scopes.