> ## Documentation Index
> Fetch the complete documentation index at: https://python4ai.codewithsiva.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Python Internals

> Deep dive into mutability, object representation, identity, and memory management

To write efficient and bug-free Python code, it helps to understand how Python works under the hood. Let's explore Python's memory model, object references, and memory management.

***

## Mutability vs. Immutability

Every variable in Python is a reference pointing to an object in memory. Objects are divided into two main categories:

### 1. Mutable Objects

A **mutable** object can be changed after it is created. Examples include:

* **Lists (`list`)**
* **Dictionaries (`dict`)**
* **Sets (`set`)**

```python theme={null}
# Modifying a list in-place (same memory address)
numbers = [1, 2, 3]
print(id(numbers))     # e.g., 4398114048

numbers.append(4)
print(numbers)         # [1, 2, 3, 4]
print(id(numbers))     # 4398114048 (Address did not change!)
```

### 2. Immutable Objects

An **immutable** object cannot be changed after it is created. If you try to modify it, Python creates a *new* object at a new memory address. Examples include:

* **Integers (`int`), Floats (`float`)**
* **Strings (`str`)**
* **Tuples (`tuple`)**
* **Booleans (`bool`)**

```python theme={null}
# Modifying a string creates a new string object
name = "Alice"
print(id(name))        # e.g., 4398201904

name += " Smith"
print(id(name))        # e.g., 4398205424 (Different address!)
```

<Warning>
  **Precaution:** Since tuples are immutable, you cannot change their elements. However, if a tuple contains a mutable object (like a list), the list elements *can* be modified!

  ```python theme={null}
  my_tuple = (1, [2, 3])
  my_tuple[1].append(4)  # This is allowed! Tuple now: (1, [2, 3, 4])
  ```
</Warning>

***

## Everything is an Object

In Python, **everything** is a first-class object. This includes integers, strings, functions, modules, and even classes themselves.

An object in Python contains three things:

1. **A Value:** The data itself (e.g., `"Hello"`).
2. **A Type:** Defines what the object can do (e.g., `<class 'str'>`).
3. **An Identity:** A unique integer representing its memory address (retrieved via `id()`).

```python theme={null}
# Even a function is an object and can be assigned to a variable!
def greet():
    return "Hello!"

func_reference = greet
print(func_reference())   # Hello!
print(type(greet))        # <class 'function'>
```

***

## Identity (`id()`) vs. Equality (`==`)

Understanding the difference between `==` and `is` is a common source of confusion:

* **Equality (`==`):** Compares the **values** of the objects (uses the `__eq__` method).
* **Identity (`is`):** Compares the **memory addresses** of the objects (checks if both variables point to the exact same object in memory).

```python theme={null}
list_a = [1, 2, 3]
list_b = [1, 2, 3]

# 1. Values are equal
print(list_a == list_b)  # True

# 2. Identities are NOT equal (they are different objects in memory)
print(list_a is list_b)  # False
print(id(list_a) == id(list_b)) # False
```

<Tip>
  **Caching Nuance (Integer Caching):** Python caches small integers (from `-5` to `256`) and small strings in memory for optimization.

  ```python theme={null}
  x = 100
  y = 100
  print(x is y)  # True (due to CPython caching!)

  a = 300
  b = 300
  print(a is b)  # False (different objects in memory)
  ```
</Tip>

***

## Reference Counting

Python manages memory automatically using **garbage collection**. The primary mechanism used is **Reference Counting**.

* Every object in memory keeps track of how many variables are referencing it.
* When you assign an object to a variable, its reference count increases by 1.
* When a variable goes out of scope or is deleted, the reference count decreases by 1.
* When the reference count drops to **0**, the object is destroyed, and the memory is reclaimed.

```python theme={null}
import sys

# Create a list object (Ref count = 1)
a = [1, 2, 3]

# Create another reference (Ref count = 2)
b = a

# sys.getrefcount() adds 1 temporary reference during call
print(sys.getrefcount(a))  # Output: 3
```

<Warning>
  **Circular References:** If Object A references Object B, and Object B references Object A, their reference counts can never drop to 0 even if they are unreachable. Python uses an auxiliary **Generational Garbage Collector** to detect and destroy these circular references.
</Warning>

***

## Practice & Exercises

To reinforce what you've learned in this section (Mutability, object types/identities, caching optimizations, and reference counting), practice with these interactive notebooks:

<CardGroup cols={2}>
  <Card title="Follow-Along Practice" icon="laptop-code">
    Practice defining mutable and immutable objects, tracking id values, appending mutable sub-items inside tuples, comparing equal values vs identities, and tracking reference counts.

    [💻 VS Code](vscode://file/Users/sivaprasad/Downloads/python%20material/python4ai/public/notebooks/basics_exercises/Python_Internals_Practice.ipynb) | [🚀 Colab](https://colab.research.google.com/github/prasad230776/python4ai/blob/master/public/notebooks/basics_exercises/Python_Internals_Practice.ipynb) | <a href="/public/notebooks/basics_exercises/Python_Internals_Practice.ipynb" download>📥 Download</a>
  </Card>

  <Card title="Practice Exercises" icon="pen-to-square">
    Test your knowledge with hands-on exercises on extending mutable lists, editing nested tuple elements, evaluating dictionary identities, and verifying small integer cache limits.

    [💻 VS Code](vscode://file/Users/sivaprasad/Downloads/python%20material/python4ai/public/notebooks/basics_exercises/Python_Internals_Exercises.ipynb) | [🚀 Colab](https://colab.research.google.com/github/prasad230776/python4ai/blob/master/public/notebooks/basics_exercises/Python_Internals_Exercises.ipynb) | <a href="/public/notebooks/basics_exercises/Python_Internals_Exercises.ipynb" download>📥 Download</a>
  </Card>
</CardGroup>

## What's next?

Now that you understand Python's internal memory structures, let's learn how functions are treated as objects, closures, and writing custom decorators!

<Card title="Advanced Functions" icon="arrow-right" href="/advanced-python/functions">
  Master lambdas, closures, and decorators
</Card>
