Синтез ДНК кажется чем-то сугубо биологическим — с пробирками, центрифугами и белыми халатами. Но что, если попробовать собрать ДНК в коде? Не просто сгенерировать последовательность, а симулировать реальные процессы: лигирование, гибридизацию, ПЦР, ошибочные вставки, ферментативные сдвиги и многое другое. В этой статье — практическая попытка воссоздать молекулярную биологию средствами Python, без библиотек типа Biopython, с нуля. Много кода, немного шуток и один вопрос — можно ли построить in silico ДНК-лабораторию?

Слова “биоинформатика” и “программирование” обычно встречаются в одном предложении, когда речь идёт о парсинге геномов, анализе экспрессии генов или машинном обучении для диагностики. Но однажды захотелось большего. Хотелось не просто читать гены, а играть с ними. Моделировать их, собирать руками. Вернее, клавиатурой.
Идея: построить в коде лабораторную скамью, где можно будет “сшивать” фрагменты ДНК, копировать их, проверять на ошибки. Причём без привычных библиотек вроде Biopython — просто Python, NumPy и желание воссоздать реальный молекулярный процесс в виртуальной среде. Можно ли собрать виртуальную ДНК-плазмиду и отдать её синтетической биологии?
Да. Но сначала немного теории.
Минимум молекулярной биологии
Если вы не биолог — не страшно. Вот краткий набор знаний, который понадобится:
ДНК состоит из 4 оснований:
A
,T
,C
,G
.A
соединяется сT
,C
сG
. Это называется комплементарностью.ДНК может быть одноцепочечной или двуцепочечной.
Ферменты могут копировать, соединять, резать или модифицировать ДНК.
Сборка ДНК — это как LEGO, только меньше и с более странными инструкциями.
Соберём свой набор инструментов.
1. Структура: последовательности и цепи
Начнём с основного объекта — молекулы ДНК. Представим её как последовательность символов, но добавим немного структуры.
Язык: Python 3.11+
from typing import List
class Strand:
def __init__(self, sequence: str):
assert all(base in "ATCG" for base in sequence), "Invalid DNA sequence"
self.sequence = sequence
def complement(self) -> str:
return self.sequence.translate(str.maketrans("ATCG", "TAGC"))
def reverse_complement(self) -> str:
return self.complement()[::-1]
def __repr__(self):
return f"5'-{self.sequence}-3'"
Пример использования:
s = Strand("ATGCGT")
print(s.reverse_complement()) # ACGCAT
Класс Strand
— это строительный кирпич. Теперь можно моделировать гибридизацию, сшивку и другие операции.
2. Гибридизация: связываем цепи
Когда две цепи встречаются и могут быть комплементарными, они связываются — образуется “дуплекс”.
def can_hybridize(s1: Strand, s2: Strand, min_overlap: int = 5) -> bool:
rc = s2.reverse_complement()
for i in range(len(s1.sequence) - min_overlap + 1):
if s1.sequence[i:].startswith(rc[:len(s1.sequence)-i]):
return True
return False
Пример:
a = Strand("ATGCGT")
b = Strand("ACGCAT")
print(can_hybridize(a, b)) # True
Здесь мы проверяем: может ли вторая цепь при перевороте и комплементации хотя бы частично приклеиться к первой.
3. ПЦР: полимеразная цепная реакция
ПЦР — это способ быстро наращивать миллионы копий ДНК с помощью фермента и температурных циклов.
from random import randint
def pcr_amplify(template: Strand, primers: List[Strand], cycles: int = 30) -> List[Strand]:
amplified = []
for _ in range(cycles):
for primer in primers:
if primer.sequence in template.sequence:
start = template.sequence.find(primer.sequence)
amplified.append(Strand(template.sequence[start:]))
return amplified
template = Strand("ATGCGTACCGTTAAGT")
primer1 = Strand("ATGCG")
primer2 = Strand("AAGT")
products = pcr_amplify(template, [primer1, primer2])
print(f"Products: {len(products)}")
Это упрощённая модель, но она демонстрирует суть: каждый цикл удваивает фрагменты, на которые садятся праймеры.
4. Лигирование: склеивание фрагментов
Фермент лигаза соединяет два фрагмента ДНК, если они совместимы (например, если один заканчивается на последовательность, с которой начинается другой).
def ligate(s1: Strand, s2: Strand) -> Strand:
# простое сшивание, без проверки комплементарности
return Strand(s1.sequence + s2.sequence)
frag1 = Strand("ATGCGT")
frag2 = Strand("ACCGTA")
ligated = ligate(frag1, frag2)
print(ligated) # 5'-ATGCGTACCGTA-3'
В реальности нужна проверка на “липкие концы” (sticky ends). Но даже простая конкатенация уже позволяет моделировать базовое склеивание.
5. Ошибки и мутации
Биология не бывает идеальной. Мутации — часть игры.
from random import choice, random
def mutate(s: Strand, rate: float = 0.01) -> Strand:
new_seq = ""
for base in s.sequence:
if random() < rate:
new_seq += choice("ATCG".replace(base, ""))
else:
new_seq += base
return Strand(new_seq)
original = Strand("ATGCGTACCGTTAAGT")
mutated = mutate(original, rate=0.1)
print(f"Original: {original}")
print(f"Mutated : {mutated}")
С мутациями можно играть: вставки, делеции, инверсии, перестройки. Особенно забавно смотреть, как они влияют на “виртуальный геном”.
6. Построим плазмиду
Соберём кольцевую ДНК, как это делают в лабораториях. В простом случае представим кольцо как фрагмент, где 3'-конец соединяется с 5'-концом.
class Plasmid(Strand):
def __repr__(self):
return f"Circular 5'-{self.sequence}-3'"
def cut(self, site: str) -> List[Strand]:
if site not in self.sequence:
return [self]
idx = self.sequence.find(site)
return [Strand(self.sequence[:idx]), Strand(self.sequence[idx:])]
plasmid = Plasmid("ATGCGTACCGTTAAGTCCGATC")
cut_fragments = plasmid.cut("CCGTTA")
for f in cut_fragments:
print(f)
Теперь мы можем разрезать плазмиды, вставлять гены и даже симулировать клонирование.
Заключение
Всё вышеприведённое — это даже не поверхность. Это царапина на внешнем слое синтетической биологии. Можно углубляться в:
Моделирование температурных профилей (анализ работы ПЦР при разных температурах).
Ферментативную кинетику (микроскопично эмулировать скорость работы ДНК-полимеразы).
Химию буферов (учёт pH, концентрации ионов взаимодействия).
Вероятность отжига праймеров (точное моделирование Tm).
И это будет только интереснее.
Главный вывод: биология прекрасно ложится на программирование. Более того, если вы программист — вы уже почти синтетический биолог. Вам просто нужно немного других библиотек… и чуть больше пипеток.
А пока можно собрать собственный геном, сшить его из фрагментов и “запустить” in silico.
P.S. Кода здесь много, но вы можете легко собрать из него полноценную симуляцию. Попробуйте, например, реализовать CRISPR. Или хотя бы фермент EcoRI.
Если нужен Jupyter Notebook с полным примером — напишите в комментариях, подготовим.