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

# Python Generator Function

A generator function is a specialized Python function that returns a lazy iterator (specifically, a generator object) instead of executing its body immediately and returning a single value. It is distinguished syntactically by the presence of one or more `yield` expressions. Rather than destroying its local state upon returning data, a generator function suspends its execution, preserves its local variables and instruction pointer, and yields control back to the caller.

```python theme={"dark"}
def basic_generator():
    # Local state is initialized
    value = "data"
    yield value  # Execution suspends here; value is emitted
    # Execution resumes here on the next iteration
```

## Core Mechanics

**1. Instantiation vs. Execution**
Invoking a generator function does not execute its code block. Instead, it instantiates and returns a generator object, which implements the Python iterator protocol (`__iter__()` and `__next__()`).

**2. State Suspension (`yield`)**
Execution of the function body begins only when `next()` (or the object's `__next__()` method) is called. The interpreter executes the code until it encounters a `yield` expression. At this point, the function evaluates the expression, returns the result to the caller, and freezes its internal state.

**3. Resumption**
Upon subsequent calls to `next()`, the generator resumes execution immediately following the last executed `yield` statement, retaining full access to its previously established local scope and bindings.

**4. Termination (`StopIteration`)**
When the generator function reaches the end of its code block or encounters a `return` statement, it automatically raises a `StopIteration` exception. This signals to the caller (or a consuming `for` loop) that the iterator is exhausted. If a `return` statement includes a value (e.g., `return "Done"`), that value is attached to the `StopIteration.value` attribute.

```python theme={"dark"}
def state_machine():
    x = 10
    yield x        # State frozen. Yields 10.
    
    x += 5
    yield x        # State frozen. Yields 15.
    
    return "End"   # Raises StopIteration("End")

gen = state_machine()  # Returns generator object
val1 = next(gen)       # val1 = 10
val2 = next(gen)       # val2 = 15

# next(gen)            # Raises StopIteration
```

## Advanced Generator Interface

Because generator functions maintain state, Python extends their capabilities beyond simple iteration, allowing them to act as lightweight coroutines. The generator object exposes three methods to manipulate its suspended state:

* **`send(value)`**: Resumes execution and injects `value` into the generator. The `yield` expression where the generator is currently paused evaluates to this injected value. (Note: The first call to a generator must be `next()` or `send(None)` to advance it to the first `yield`).
* **`throw(type, [value, [traceback]])`**: Resumes execution by raising the specified exception at the exact point of the suspended `yield`. The generator function can catch this exception using a `try/except` block.
* **`close()`**: Raises a `GeneratorExit` exception at the suspended `yield`. If the generator catches this exception, it must either raise `GeneratorExit`, raise `StopIteration`, or cleanly exit. A `RuntimeError` is raised *only* if the generator attempts to `yield` another value after catching `GeneratorExit`. If the generator raises any other exception during closure, that exception is propagated to the caller.

```python theme={"dark"}
def bidirectional_generator():
    try:
        # Pauses and waits for a value via .send()
        received = yield "Initialized"
        yield f"Processed: {received}"
    except ValueError:
        # Executes if .throw(ValueError) is called at the first yield
        yield "Exception Handled"


# Demonstrating send()
gen1 = bidirectional_generator()
print(next(gen1))              # Outputs: Initialized
print(gen1.send("Data"))       # Outputs: Processed: Data


# Demonstrating throw()
gen2 = bidirectional_generator()
print(next(gen2))              # Outputs: Initialized
print(gen2.throw(ValueError))  # Outputs: Exception Handled
```

## Delegation with `yield from`

A generator function can delegate part of its operations to another generator (or any iterable) using the `yield from` syntax. This establishes a transparent, bidirectional communication channel between the caller and the sub-generator, automatically handling `next()`, `send()`, `throw()`, and `close()` routing, as well as catching the sub-generator's `StopIteration` value.

```python theme={"dark"}
def sub_generator():
    yield 1
    yield 2
    return "Sub Complete"

def delegating_generator():
    yield "Start"
    # Yields all values from sub_generator, captures its return value
    result = yield from sub_generator() 
    yield result

gen = delegating_generator()

# Yields sequence: "Start", 1, 2, "Sub Complete"
```

<div
  style={{ 
display: "flex", 
justifyContent: "space-between", 
alignItems: "center", 
maxWidth: "754px", 
padding: "1rem 0",
marginBottom: "24px"
}}
>
  <span style={{ fontWeight: "bold", fontSize: "1.25rem", color: "var(--tw-prose-headings)", fontFamily: "Inter, ui-sans-serif, system-ui, sans-serif" }}>Tired of Poor Python Skills? Fix That With Deep Grasping!</span>

  <a
    href="https://syntblaze.com"
    target="_blank"
    style={{ 
  marginLeft: "24px",
  textDecoration: "none", 
  backgroundColor: "#007AFF",
  color: "#ffffff", 
  padding: "6px 16px", 
  borderRadius: "16px",
  fontSize: "0.9rem",
  fontWeight: "600",
  textAlign: "center",
  transition: "background-color 0.2s ease"
}}
  >
    Learn More
  </a>
</div>

<div style={{ display: "flex", gap: "12px", flexWrap: "wrap" }}>
  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/skill-tracking.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=b9b0305c93bb501c9e767b5c76c88835" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/skill-tracking.png" />

  <img src="https://mintcdn.com/syntblazellc/23tyuOzaWS88qFlc/images/nuggets.png?fit=max&auto=format&n=23tyuOzaWS88qFlc&q=85&s=c86c80197299762989e9b882419b2109" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/nuggets.png" />

  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/bite-sized-exercises.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=a65f9a38c37ff28ab73ed783c53c60e3" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/bite-sized-exercises.png" />
</div>

<div style={{ display: "flex", gap: "12px", flexWrap: "wrap", marginTop: "12px" }}>
  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/mastery-chain.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=748a1763454713e679260fbb95f154a2" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/mastery-chain.png" />

  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/element-previews.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=242f61448ff5dd6deaaab2dccc13b507" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/element-previews.png" />

  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/element-explanations.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=cf0fc1c31f9cd0fc26716781be05fbc9" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/element-explanations.png" />
</div>
