# Python Bio Lists

> Split CDS into codons, extract k-mers, sort sequences by GC%/length, and pack gene coordinates into tuples/namedtuples. Use when looping over genes/codons/SNPs/BED intervals, computing sliding-window GC%, or detecting gene overlaps in Python.

- Skill: `pavel-kravchenko/python-bio-lists` (Agent Skill)
- Install (CLI): `npx skillmds@latest add pavel-kravchenko/python-bio-lists`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pavel-kravchenko/python-bio-lists/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: pavel-kravchenko (https://skillmd.com/u/pavel-kravchenko)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/pavel-kravchenko/python-bio-lists

---


# Lists and Tuples for Bioinformatics

## When to Use

- Splitting a coding sequence (CDS) into codons or extracting sliding k-mers from DNA/RNA.
- Sorting a batch of sequences or genes by GC content, length, or any derived key.
- Representing a fixed biological record (gene coordinates, a SNP call, a BED interval) that should not be accidentally mutated.
- Building readable gene/variant records with `namedtuple` instead of unlabeled index access (`gene[2]`).
- Detecting overlaps between genomic intervals stored as `(name, start, end)` tuples.

## Version Compatibility

Pure Python stdlib — no third-party dependency. Works on Python ≥3.8 (relies on `collections.namedtuple`, f-strings, and stable dict ordering; all present since 3.7+).

## Prerequisites

- No packages to install — everything here is Python builtins plus `collections` from the standard library.
- Familiarity with string indexing/slicing (see `python-bio-strings`) and basic `for` loops.

**Goal:** split a CDS into codons and pull every k-mer of length *k* out of a sequence.

**Approach:** step the index by 3 (codons) or by 1 (k-mers), slicing the string at each position; guard against a trailing partial codon with `len(seq) % 3`.

```python
def split_codons(cds: str) -> list[str]:
    """Split a coding DNA sequence into complete triplet codons.

    Any trailing 1-2 leftover bases (incomplete codon) are dropped.
    """
    return [cds[i:i + 3] for i in range(0, len(cds) - len(cds) % 3, 3)]


def extract_kmers(sequence: str, k: int) -> list[str]:
    """Return all overlapping k-mers of length k, in order of appearance."""
    return [sequence[i:i + k] for i in range(len(sequence) - k + 1)]


cds = "ATGGCCGATCGATAGCCA"
print(split_codons(cds))                # ['ATG', 'GCC', 'GAT', 'CGA', 'TAG', 'CCA']
print(extract_kmers("ATGATGATG", 3))    # ['ATG', 'TGA', 'GAT', 'ATG', 'TGA', 'GAT', 'ATG']
```

**Goal:** rank a collection of sequences or genes by a biological property (GC%, length).

**Approach:** write a small key function and pass it to `sorted(..., key=...)` — never mutate the input with `.sort()` unless you explicitly want that.

```python
def gc_content(seq: str) -> float:
    """Fraction of G/C bases in seq, in [0, 1]."""
    return (seq.count("G") + seq.count("C")) / len(seq)


sequences = ["ATATATAT", "GCGCGCGC", "ATGCATGC", "AAAGGGCCC", "TTTTAAAA"]
by_gc = sorted(sequences, key=gc_content)              # ascending GC%
by_gc_desc = sorted(sequences, key=gc_content, reverse=True)

gene_data = [("BRCA1", 81189), ("TP53", 19149), ("EGFR", 188307)]
longest = max(gene_data, key=lambda g: g[1])            # ('EGFR', 188307)
by_length = sorted(gene_data, key=lambda g: g[1], reverse=True)
```

**Goal:** store fixed genomic records (gene coordinates) and find overlapping intervals.

**Approach:** use plain tuples for lightweight `(name, start, end)` records, `namedtuple` when you want `.start`/`.end` field access, and a simple O(n^2) pairwise scan for overlap detection on small interval lists (for large interval sets use an interval tree — see `bio-genome-intervals-interval-arithmetic`).

```python
from collections import namedtuple

Gene = namedtuple("Gene", ["name", "chromosome", "start", "end", "strand"])

brca1 = Gene("BRCA1", "chr17", 43044295, 43125483, "-")
print(brca1.end - brca1.start)  # access by name, not index -> 81188


def find_overlapping_genes(genes: list[tuple[str, int, int]]) -> list[tuple[str, str, int]]:
    """Find all pairs of overlapping (name, start, end) intervals.

    Returns [(name_a, name_b, overlap_bp), ...] for every overlapping pair.
    O(n^2) — fine for dozens to low thousands of intervals.
    """
    overlaps = []
    for i in range(len(genes)):
        for j in range(i + 1, len(genes)):
            name_a, start_a, end_a = genes[i]
            name_b, start_b, end_b = genes[j]
            overlap_start = max(start_a, start_b)
            overlap_end = min(end_a, end_b)
            if overlap_start < overlap_end:
                overlaps.append((name_a, name_b, overlap_end - overlap_start))
    return overlaps


test_genes = [("geneA", 100, 500), ("geneB", 400, 800), ("geneC", 900, 1200)]
print(find_overlapping_genes(test_genes))  # [('geneA', 'geneB', 100)]
```

## Pitfalls

- **Assignment does not copy:** `alias = original` makes both names point to the same list; `alias.append(...)` mutates `original` too. Use `original.copy()` or `original[:]` for an independent copy.
- **`append()` vs `extend()`:** `genes.append(["BRCA1", "TP53"])` adds a list as one element; `genes.extend(["BRCA1", "TP53"])` adds two elements.
- **`sort()` returns `None`:** it mutates in place and returns `None` — chaining `x = my_list.sort()` silently gives `x = None`. Use `sorted()` when you need the original preserved.
- **Trailing comma required for a 1-tuple:** `("BRCA1")` is just a string; `("BRCA1",)` is a tuple.
- **Off-by-one on the last codon/k-mer:** forgetting `- len(seq) % 3` (codons) or `- k + 1` (k-mers) either crashes with a short slice or silently drops the final complete unit.
- **Tuples are immutable but hashable:** use them for fixed records (coordinates, SNPs) and as dict keys; lists cannot be dict keys.

## See Also

- `python-bio-tuples` — deeper dive on packing/unpacking and named tuples.
- `python-bio-dictionaries` — pairing these records with fast name/coordinate lookups.
- `python-bio-comprehensions` — list/dict comprehensions for the same codon/k-mer patterns.
- `bio-genome-intervals-interval-arithmetic` — scalable interval overlap for large BED-style datasets.

