DNA to protein, by coding the central dogma
Every cell in your body runs the same program: read DNA, make protein. Biologists call it the central dogma, and it sounds like deep chemistry. Computationally, it is almost embarrassingly simple: copy a string with one letter swapped, then walk it three characters at a time through a lookup table. Build it and the machinery of life becomes a program you can read.
The one idea
DNA stores instructions as a sequence of four bases: A, C, G, T. To build a protein, the cell does two steps. Transcription copies a gene into RNA, identical except T becomes U. Translation reads the RNA in groups of three bases called codons, and each codon maps to one amino acid, the beads that string together into a protein. The genetic code is just a dictionary from 64 codons to 20 amino acids, plus start and stop signals.
Transcription: one substitution
DNA to RNA is a single letter swap.
def transcribe(dna):
return dna.replace("T", "U")
print(transcribe("ATGGCCTAA")) # ATG GCC TAA
# AUGGCCUAA
That is genuinely all transcription is, at the sequence level. The biology is elaborate; the information transformation is one substitution.
Translation: a sliding window over a codon table
Now the real work. We need the codon table (a slice shown here; the full one has all 64), then we walk the RNA three bases at a time, starting at the AUG start codon and stopping at the first stop codon.
CODONS = {
"AUG": "M", # Methionine, also the START codon
"GCC": "A", "GCU": "A", "GCA": "A", "GCG": "A", # Alanine
"UUU": "F", "UUC": "F", # Phenylalanine
"GGU": "G", "GGC": "G", "GGA": "G", "GGG": "G", # Glycine
"AAA": "K", "AAG": "K", # Lysine
"UAA": "*", "UAG": "*", "UGA": "*", # STOP
}
def translate(rna):
start = rna.find("AUG")
if start == -1:
return ""
protein = []
for i in range(start, len(rna) - 2, 3):
codon = rna[i:i + 3]
amino = CODONS.get(codon, "?")
if amino == "*": # stop codon: end the protein
break
protein.append(amino)
return "".join(protein)
Run the central dogma end to end
Take a strand of DNA, transcribe it, translate it.
gene = "TACGATGGCCTTTGGCAAATAAGGG" # a DNA strand (leading bases, then a gene)
rna = transcribe(gene)
print("RNA: ", rna)
print("protein:", translate(rna))
RNA: UACGAUGGCCUUUGGCAAAUAAGGG
protein: MAFGK
Read what happened: translation ignored the leading UACGA junk, found the AUG start, then read codons in frame, GCC, UUU, GGC, AAA, giving M-A-F-G-K, and halted at the UAA stop, leaving the trailing GGG unread. That is a real, if short, protein, produced by the same two operations every one of your cells performs billions of times a second.
Why the small details are the whole game
Two things in that code carry deep biology. First, the reading frame: codons are non-overlapping triples, and where you start changes everything. Start one base over and AUGGCC becomes UGGCCU, a completely different protein. This is why a single inserted base, a frameshift mutation, can be catastrophic: it reshuffles every codon downstream. Second, the code is redundant: four different codons all mean Alanine. That redundancy is a buffer, many single-letter mutations change the codon but not the amino acid, so the protein is unharmed.
You just encoded the process behind genetics, evolution, and every genetic disease, as a substitution and a dictionary lookup. That is the promise of computational biology: the mechanisms of life are algorithms, and you can run them. Building them from the sequence up is exactly how the bioinformatics track works.