Optimize Python CLI Performance with Lazy Imports

You’ve built a beautiful CLI with Typer. It’s got great commands, helpful documentation, and does exactly what you need. There’s just one problem: running cli --help takes 3 seconds. Tab completion feels sluggish.

The culprit? Eager imports loading heavy dependencies before they’re needed.

Let’s fix that.

The Problem: Death by a Thousand Imports

Here’s a common pattern I see in CLI applications:

# ❌ BAD: All imports at the top
import typer
import os
import sys
import torch
import transformers
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from PIL import Image

app = typer.Typer()

@app.command()
def predict(image_path: str):
    """Run ML prediction on an image."""
    model = torch.load("model.pt")
    img = Image.open(image_path)
    # ... prediction logic

@app.command()
def train(data_path: str):
    """Train a new model."""
    df = pd.read_csv(data_path)
    # ... training logic

if __name__ == "__main__":
    app()

When you run cli --help, Python dutifully loads PyTorch, Transformers, Pandas, NumPy, scikit-learn, and Pillow before showing you the help text. That can easily take 2-5 seconds on a cold start. To make matters worse, all of this happens during tab-completion too.

The Solution: Lazy Loading

The fix is simple: only import what you need, when you need it.

# ✅ GOOD: Lazy imports inside functions
import typer

app = typer.Typer()

@app.command()
def predict(image_path: str):
    """Run ML prediction on an image."""
    # Import only when this command is actually run
    import torch
    from PIL import Image
    
    model = torch.load("model.pt")
    img = Image.open(image_path)
    # ... prediction logic

@app.command()
def train(data_path: str):
    """Train a new model."""
    import pandas as pd
    
    df = pd.read_csv(data_path)
    # ... training logic

if __name__ == "__main__":
    app()

Now cli --help loads in milliseconds. Tab completion is instant. Your users are happy.

Be Selective: Not All Imports Are Equal

Not every import needs to be lazy. Some modules load instantly, and putting them inside functions just adds clutter:

# ✅ Fast imports: Keep at the top
import os
import sys
import json
import re
import pathlib
from typing import Optional, List
import typer

# ❌ Slow imports: Move inside functions
# import torch
# import transformers
# import pandas
# import numpy
# import PIL
# import cv2
# import tensorflow

Rule of thumb: If it’s in the Python standard library or is a lightweight pure-Python package, it’s probably fine at the top. If it’s a heavy data science, ML, or image processing library, lazy load it. I often experiment a bit to find which packages are slow and selectively lazy-load those.

Measuring the Impact

Don’t guess—measure! Use the time command to see the difference:

# Before optimization
$ time cli --help
real    0m3.247s
user    0m2.891s
sys     0m0.342s

# After optimization
$ time cli --help
real    0m0.124s
user    0m0.098s
sys     0m0.024s

That’s a 26× speedup! For tab completion, which calls your CLI repeatedly, this difference is make-or-break.

Leave a Reply