Python Operators for Bioinformatics
When to Use
- Computing GC content, codon counts, or reading frames from sequence lengths/positions
- Filtering sequences by multiple QC criteria (length, GC%, ambiguous bases) in one expression
- Checking membership — is a codon a stop codon, is a base valid, is a key in a codon table
- Debugging a wrong GC% or Tm calculation caused by operator precedence (
g + c / total * 100) - Choosing
==vsis, orlistvssetmembership, for repeated sequence/codon lookups
Version Compatibility
Python ≥3.8 (f-strings). No external packages required — pure built-in operators.
Prerequisites
- Basic Python: variables, strings, lists, dicts, sets, functions
- No other skill required; this is foundational for
python-bio-strings,python-bio-sequences
Codon Arithmetic and Reading Frames
Goal: Given a sequence length or a position, get the number of complete codons, leftover bases, and which of the three reading frames a position falls in.
Approach: // (floor division) always returns an int and gives complete units; /
always returns a float. % gives the remainder, which doubles as the reading-frame index.
def codon_stats(seq_length: int, position: int = 0) -> dict:
"""Return codon-counting stats for a sequence length and a query position.
seq_length // 3 -> number of complete codons (int, not float — use // not /)
seq_length % 3 -> leftover nucleotides after the last complete codon
position % 3 -> reading frame (0, 1, or 2) that `position` falls in
"""
return {
"complete_codons": seq_length // 3,
"leftover_nucleotides": seq_length % 3,
"reading_frame": position % 3,
}
stats = codon_stats(seq_length=1000, position=47)
print(stats) # {'complete_codons': 333, 'leftover_nucleotides': 1, 'reading_frame': 2}
GC Content and Multi-Criteria Sequence Filtering
Goal: Compute GC content without a precedence bug, then combine it with length and ambiguous-base checks in a single readable boolean expression.
Approach: Parenthesize (g + c) explicitly — * and / bind tighter than +, so
g + c / total * 100 silently computes g + (c / total * 100). Use chained comparisons
(a <= x <= b) instead of x >= a and x <= b, and set literals for O(1) membership tests.
def gc_content(sequence: str) -> float:
"""Return GC content as a percentage. Precedence-safe: (g + c) is parenthesized."""
seq = sequence.upper()
g, c = seq.count("G"), seq.count("C")
total = len(seq)
return (g + c) / total * 100 if total else 0.0
def passes_qc(sequence: str, has_ambiguous_bases: bool = False) -> bool:
"""Multi-criteria QC filter: length 500-3000 nt, GC 30-70%, no ambiguous bases."""
seq_length = len(sequence)
gc = gc_content(sequence)
return (
500 <= seq_length <= 3000
and 30.0 <= gc <= 70.0
and not has_ambiguous_bases
)
def is_stop_codon(codon: str) -> bool:
"""O(1) membership test against a set literal (use a set, not a list, for lookups)."""
return codon.upper() in {"TAA", "TAG", "TGA"}
Primer Melting Temperature (Wallace Rule)
Goal: Estimate a short primer's melting temperature — a common downstream use of the same arithmetic/precedence rules.
Approach: Tm = 2*(A+T) + 4*(G+C) for primers under 14 nt (Wallace rule).
def melting_temperature(primer: str) -> float:
"""Estimate Tm (degrees C) for a short PCR primer using the Wallace rule."""
p = primer.upper()
a, t, g, c = p.count("A"), p.count("T"), p.count("G"), p.count("C")
if len(p) < 14:
return 2 * (a + t) + 4 * (g + c)
return 64.9 + 41 * (g + c - 16.4) / (a + t + g + c)
Pitfalls
//vs/for codons:seq_length // 3gives complete codons (int)./always returnsfloat, which breaks anything expecting an integer codon count or index.%for reading frames:position % 3gives the frame (0, 1, or 2);seq_length % 3gives leftover nucleotides — don't confuse the two uses of the same operator.- Precedence trap in GC content:
g + c / total * 100is wrong (division binds before+). Always write(g + c) / total * 100. ==vsis: use==for value comparison;isonly forNone/True/False. Never writeif x == Noneorif my_list is [1, 2, 3].inchecks dict keys, not values:"ATG" in codon_tablechecks keys. Use"Met" in codon_table.values()to check values (and it's O(n), not O(1)).inwith sets is O(1), with lists is O(n): convert to asetbefore repeated membership tests (e.g. stop-codon or valid-nucleotide checks in a loop over a chromosome).and/orshort-circuit:passes_qc(seq) and log_pass(seq)won't calllog_passifpasses_qcisFalse— fine for filters, a bug if the right side has required side effects.
See Also
python-bio-expressions— building larger expressions and boolean logicpython-bio-strings— string methods (.count(),.upper(), slicing) used abovepython-bio-sequences— sequence objects that wrap these operationsbio-primer-design-primer-basics— full primer design beyond the Wallace-rule estimate