Python pass Statement: Stubs, Empty Blocks and Loops

The Python pass statement does nothing. It’s a placeholder you put where Python needs a statement but you have no code yet, like an empty function, class, loop or if branch. People often call it the pass function, but it’s a keyword:

def calculate_sales_tax(subtotal, state):
    pass    # body comes later

Each snippet here got tested with Python 3.12.5 inside a Windows Command Prompt, and the output blocks are pasted straight from those runs.

In this tutorial, we’ll cover stub functions and classes, the error you get without pass, how it differs from continue, break and ..., and when using it in an except block is fine.

Use the pass statement in an empty function, class or if branch

A block in Python can’t be empty, because indentation is what marks the block. If you’re sketching a program, pass fills the body so the file still runs. New to def? Start with how to define a function in Python:

def calculate_sales_tax(subtotal, state):
    pass                      # TODO: add state tax tables

class PaymentDeclined(Exception):
    pass                      # a custom error needs no body

class ShoppingCart:
    def apply_coupon(self, code):
        pass                  # planned for the next release

print(calculate_sales_tax(120.00, "TX"))
print(ShoppingCart().apply_coupon("SAVE10"))

for total in (18.75, 64.00, 250.00):
    if total < 25:
        pass                  # small orders: nothing to do yet
    elif total < 100:
        print(f"${total:.2f}: standard shipping")
    else:
        print(f"${total:.2f}: free shipping")

try:
    raise PaymentDeclined("card ending 4242 was declined")
except PaymentDeclined as err:
    print("PaymentDeclined:", err)

Output:

None
None
$64.00: standard shipping
$250.00: free shipping
PaymentDeclined: card ending 4242 was declined
Command Prompt output of Python stub functions, a custom exception class and an if branch that use the pass statement
Both stubs print None, the small order prints nothing, and the empty exception class still works.

A function whose body is only pass returns None, just like one that never hits a return. That’s the same idea behind a void function in Python.

Custom exceptions are the one place I keep pass for good: class PaymentDeclined(Exception): pass is a complete class. For classes that need setup, see an empty constructor in Python.

How to run the pass statement examples yourself

If you’ve never run a Python file from the Command Prompt, here’s the short version. You only need Python installed from python.org with the PATH option checked:

  1. Open Notepad or VS Code and paste the first example.
  2. Save it as shop_stubs.py in a folder such as C:\pyguides.
  3. Press Win + R, type cmd and press Enter.
  4. Type cd C:\pyguides, then python shop_stubs.py, and press Enter.
  5. Compare what prints with the output block above, then try deleting a pass line to see the error below.

What happens if a Python function body is empty without pass?

A comment doesn’t count as a statement; the parser throws it away. So a function that holds only a comment fails before a single line of the file runs:

def send_invoice(customer):
    # write this later

print("never reached")

Output:

  File "C:\pyguides\empty_body.py", line 4
    print("never reached")
    ^
IndentationError: expected an indented block after function definition on line 1
Command Prompt output of IndentationError expected an indented block after a Python function with only a comment
Python stops at the next unindented line, and never reached doesn’t print.

Put pass under the comment and the error goes away. The same fix works for an empty for, while, with or try block.

You’ll mostly hit this in branches, so it helps to know how if-else works in Python and where each block starts and ends.

Python pass vs continue vs break in a loop

This is where pass trips people up. It doesn’t skip anything: the next line in the loop still runs. continue jumps to the next item, and break ends the loop. More detail in Python break and continue:

orders = [("A-101", 42.50), ("A-102", 0.00), ("A-103", 19.99), ("A-104", -5.00), ("A-105", 88.00)]

print("pass:")
for order_id, total in orders:
    if total == 0:
        pass                  # does nothing, the next line still runs
    print(" ", order_id, f"${total:.2f}")

print("continue:")
for order_id, total in orders:
    if total == 0:
        continue              # skips the rest of this iteration
    print(" ", order_id, f"${total:.2f}")

print("break:")
for order_id, total in orders:
    if total < 0:
        break                 # leaves the loop for good
    print(" ", order_id, f"${total:.2f}")

Output:

pass:
  A-101 $42.50
  A-102 $0.00
  A-103 $19.99
  A-104 $-5.00
  A-105 $88.00
continue:
  A-101 $42.50
  A-103 $19.99
  A-104 $-5.00
  A-105 $88.00
break:
  A-101 $42.50
  A-102 $0.00
  A-103 $19.99
Command Prompt output comparing the Python pass, continue and break statements in a for loop over orders
With pass the $0.00 order still prints; continue skips it; break stops before the negative total.

Inside a Python for loop, I only use pass as a temporary filler. If an if branch exists just to hold pass, flipping the condition is usually cleaner.

One more difference: pass is a statement, not a value or a function. Writing value = pass doesn’t run at all:

value = pass

Output:

  File "C:\pyguides\pass_value.py", line 1
    value = pass
            ^^^^
SyntaxError: invalid syntax

Python pass vs … (Ellipsis) and NotImplementedError

Three dots, ..., also work as a stub body. They’re the built-in Ellipsis object, so Python sees an expression that does nothing. raise NotImplementedError is the loud option: callers find out right away:

def refund_order(order_id):
    ...                       # Ellipsis as a stub body

def ship_order(order_id):
    raise NotImplementedError("ship_order is not written yet")

print(refund_order("A-101"))
print(repr(...), type(...).__name__, ... is Ellipsis)
print(len([1, 2, ...]))       # ... is a real object you can store

try:
    ship_order("A-101")
except NotImplementedError as err:
    print("NotImplementedError:", err)

Output:

None
Ellipsis ellipsis True
3
NotImplementedError: ship_order is not written yet
Command Prompt output of a Python stub that uses Ellipsis and a function that raises NotImplementedError
The Ellipsis stub returns None, ... is a real object, and the unfinished function raises an error.

My rule of thumb: ... in type stubs and protocols, pass in empty classes and branches, and NotImplementedError for methods a subclass must override. For enforced overrides, use abstract base classes with abc.

Using pass in an except block to ignore errors

except ValueError: pass swallows the error and moves on. That’s fine when the bad value is expected and harmless, like blank prices in a CSV. contextlib.suppress does the same job in one line:

from contextlib import suppress

prices = ["19.99", "N/A", "42.50", "", "7.25"]

total = 0.0
for raw in prices:
    try:
        total += float(raw)
    except ValueError:
        pass                  # skip prices that aren't numbers
print(f"total with pass: ${total:.2f}")

total = 0.0
for raw in prices:
    with suppress(ValueError):
        total += float(raw)
print(f"total with suppress: ${total:.2f}")

Output:

total with pass: $69.74
total with suppress: $69.74

Both loops skip N/A and the blank string, then add the three real prices. Never write a bare except: pass, though. It hides typos and even KeyboardInterrupt, and you’ll lose hours chasing a bug that never shows up.

Does the pass statement slow down Python code?

No. The compiler drops pass entirely, so the bytecode is the same as an explicit return None. A lone ... is dropped too:

def with_pass():
    pass

def with_ellipsis():
    ...

def with_return():
    return None

print(with_pass.__code__.co_code == with_return.__code__.co_code)
print(with_ellipsis.__code__.co_code == with_return.__code__.co_code)
print(with_pass(), with_ellipsis(), with_return())

Output:

True
True
None None None

So there’s no reason to remove pass for speed. Remove it once the block has real code, since it’s just noise then. If you’re filling in a stub that should stop early, read how to exit a function in Python.

A few more function guides pair well with this one:

Frequently asked questions

What does pass do in Python?

Nothing. It’s a null statement that fills a block where Python requires at least one statement.

Is pass a function in Python?

No. It’s a keyword and a statement, so you write pass without parentheses and can’t assign it.

What is the difference between pass and continue?

pass does nothing and the loop body keeps running. continue skips to the next iteration.

Should I use pass or … in an empty function?

Both work and compile the same. Use pass in regular code and ... in type stubs and protocols.

Can I leave a function empty with just a comment?

No. Comments aren’t statements, so you get an IndentationError. Add pass under the comment.

Where is the pass statement documented?

See the pass statement in the Python language reference.