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

