Учебная страница курса биоинформатики,
год поступления 2018
1. Тригонометрия
Навыки
- подключение модуля (своего или системного)
- краткие описание функций модуля
Вызовите python как калькулятор
>>> import math # Импортируйте модуль math >>> dir(math) # чтобы получить список функций из модуля >>> help(math.sin) # для подсказки по функции math.sin()
2. Случайная последовательность
Навыки
- Подключите модуль random. ВСе остальное - как в 1.
- добавление символа к концу строки. Пример:
>>> s = "ABCD" >>> s += "E" >>> s 'ABCDE'
- Получение фиксированного участка (среза) строки:
>>> s = "ABCDEFGHIJRL" >>> i = 5 >>> fragment = s[i:i+5] >>> fragment 'FGHIJ'
- Запись в файл
g = open("randomseq.fasta", "w") g.write(">random\n") g.write(fragment + "\n") ....... g.close()
Протестируйте программу на случайной последовательности большой длины (>1000 п.н.). Поймете, зачем разбивать ее по 60 в строке
3. Вещественное число?
Используйте обработку исключений
Только так не надо:
try: <команды> except: print(<ваше сообщение - если ошибка>) exit() # если здесь надо прервать выполнение программы <дальнейшие команды>
Во-первых, четко указывайте ошибку, которую хотите поймать. Допустим, в блоке try возможно деление на 0. Так и ожидайте деления на ноль, а не вообще любую ошибку на свете. Во-вторых, exit по умолчанию возвращает 0, то есть говорит о том, что ваша программа закончилась в штатном режиме. Так ли это? Зависит от вашей программы. Но в любом случае об этом надо сообщать ЯВНО.
try ZeroDivisionError: <команды> except: print(<ваше сообщение - если ошибка>) exit(0) # если здесь надо прервать выполнение программы и в принципе работа закончилась штатно # exit(1) (или любое другое число, пока не важно), если ваша программа упала и ее работа не закончилась штатно # (в случае тестов в системе - все, что в тестах прописано - штатная ситуация) <дальнейшие команды>
4. Сумма всех чисел из файла
Навыки
- чтение файла по строкам (см. подсказки к пред. практик.)
- разбиение строки на слова; метод строки split() без аргумента или с аргументом None, делает из строки список слов, ограниченных "whitespace symbols" - то, что нам требуется!
- циклом по списку переводим каждое слово в десятичное число с помощью float(). В теле цикла ошибки перехватываем с помощью
try: <команда> except <имя_той_ошибки, которая возникает, при попытки строку, не представляющую число, в число преобразовать>: # это слово - не число continue # переход к следующему слову ничего не делая <прибавление числа к сумме и увеличение счетчика на 1>
5. Убрать невидимые символы из последовательности
Чтобы убрать все whitespaces можно применить такой трюк. Разбить строку по словам, как в подсказке к заданию 4, а потом объединить все слова в одну строку
Пусть строка s
Навыки
- оголить строку - убрать концевые whitespaces:
s.strip()
- разбить полученную строку по whitespaces
s.strip().split()
- собрать список слов в одно слово без разделителей:
clean_s = "".join(s.strip().split())
метод строки "x".join(<список строк>) имеет аргументом список строк (слов) и объединяет все слова в одну строку с разделителем "x". В данном примере х = "" - пустая строка.
Советы и подсказки к заданиям практикума 9
Подключение модулей
Подключение модуля (библиотеки) — команда import, например import math. В интерактивном режиме после этого можно вызвать список функций (и других объектов, например констант) модуля командой
dir(math)
Смысл конкретной функции (например, exp из модуля math) можно посмотреть командой
help(math.exp)
Правда, с константами так не получается :(.
- В программе, если вам нужны одна-две функции из модуля (например, только sin и cos из math), то пишете так:
from math import sin, cos ... a = cos(x) + sin(x) ...
Если же (как обычно в случае math), понадобится много чего, то пишете в программе так же, как и в интерактивном режиме:
import math ... a = math.cos(x) + math.sin(x) ...
Кроме модуля math, вам понадобится модуль random. В модуле random имеется функция, которая возвращает случайный элемент списка. Найдите её сами с помощью dir и help. Если вам нужно случайное целое число из заданного диапазона, можно применить ту же функцию к результату встроенной функции range (хотя есть и другой способ: в модуле random имеется соответствующая функция).
Список доступных модулей см. здесь: https://docs.python.org/3/library/index.html .
Форматирование числа
Если переменная mynum имеет тип float, то выражение format(mynum, ".3f") имеет значение строки, выражающей mynum с тремя знаками после десятичной точки. Полное описание того, что может стоять в качестве второго аргумента встроенной функции format, см. здесь, но если вы не всё там поняли, сильно не расстраивайтесь :)
Есть два других способа форматирования чисел. Разделы %-форматирование и str.format() описывают их. Для зачёта их знать не обязательно.
Часто вам хочется не просто отформатировать какое-то число, а вставить значение переменной в строку на определенную позицию.
%-форматирование (дополнительная информация)
Исторически первый способ, появившийся в Python. Просто ставим на то место, куда потом нужно будет вставить переменную, знак %, а после него — символ, обозначающий тип данной переменной:
%s – строка
%d – целое число
%f – вещественное число
%.2f – вещественное число, вставить с точностью до 2-го знака после запятой.
После строки снова тавится знак %, а затем — кортеж из нужных переменных.
str.format() (дополнительная информация)
Данный способ более удобен, нежели предыдущий, т.к позволяет повторно использовать переменные. Кроме того, есть и другие плюшки.
1 my_beat_str = "{} {} {} {:.1f}".format("hi", 4, 5.789, 5.789)
2 print my_beat_str
3 #hi 4 5.789 5.8
4 my_beat_str = "{0} {1} {2} {2:.1f}".format("hi", 4, 5.789) # specify position of argument in format
5 print my_beat_str
6 #hi 4 5.789 5.8
7 my_beat_str = "{string} {integer} {float} {float:.1f}".format(string="hi", integer=4, float=5.789) # pass as keywords
8 print my_beat_str
9 #hi 4 5.789 5.8
И это ещё не всё: в Python3 появился нагло позаимствованный из Perl четвертый способ форматирования вывода — f-strings — https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep498 ) Стоит заметить, что этот способ постепенно вытесняет в использовании остальные, однако он доступен только начиная с версии 3.6, что может привести к проблемам
Запись информации в файл
Чтобы писать в файл, его нужно открыть на запись командой
outf = open("file.txt", "w")
Если открываемый файл существовал ранее, то его старое содержимое при этом исчезнет! После этого переменная outf имеет тип file и представляет собой "выходной поток".
(Как ещё можно открыть файл, см. в документации).
Писать в выходной поток (в нашем случае — в файл, открытый на запись) можно методом outf.write(строка). Внимание: метод write работает не так, как оператор print (поэкспериментируйте в интерактивном режиме, чтобы увидеть различия). В частности, print принимает любые значения, а write — только строки. Но и со строками write обращается несколько иначе.
Как при печати на консоль, так и при выводе в файл могут понадобиться байты "табулятор" (удобен для вывода таблиц) и "перенос строки" (без него вообще почти невозможно обойтись).
Байт "табулятор" вводится в строку выражением \t, например:
print(str(a) + "\t" + str(b))
напечатает значения переменных a и b, разделённые табулятором.
Перенос строки — "\n" в нужном месте строки.
Полезно привыкнуть сразу после окончания чтения из файла или записи в файл закрывать его методом close, например:
outf.close()
Дополнительная информация.
Вообще-то все открытые файлы автоматически закрываются, когда программа заканчивает работу. И потому все будет прекрасно работать, пока вы не решите создавать в своей программе промежуточные файлы, информация из которых затем используется в программе. Тут внезапно выяснится, что вы что-то записали в файл, не закрыв его, этого там не оказалось, ваша программа не нашла, чего искала, и упала. Потому файлы надо закрывать.
Дело в том, что операции чтения/записи с жесткого диска не являются самыми быстрыми в мире. Потому система предпочитает записывать на жесткий диск не каждый раз, когда вы указали ей это сделать, а ждать, пока не наберется достаточно большой кусок данных. Потому в незакрытый файл вполне возможно запись произведена до конца работы программы и не будет.
Если программа в середине работы упадет, то тем более никто не обещает, что в выходной файл запишется вся информация, которую программа всё же успела обработать.
Более того, часто в биоинформатике вам приходится писать программы, работающие долгое время, возможно, отвечая на тысячи задач. Если так случится, что вы забываете на каждый запрос закрыть файл, то в какой-то момент вы не сможете открыть новый файл и ваша программа завершится аварийно. По-умному такая ситуация называется "утечка файловых дескрипторов".
Способ избежать держания в (своей) памяти необходимости закрыть файл (дополнительная информация)
Для того, чтобы уменьшить число ошибок, связанных с незакрытием файлов (), в Python существует конструкция with ... as
Словари
Словарь (dict) – это тип данных, хранящий соответствие одних значений другим (ключам словаря). Чтобы получить значение из словаря, надо написать имя словаря и в квадратных скобках — ключ (выглядит это как вызов элемента списка по индексу)
Примеры (обратите внимание: в примере встречаются три типа скобок)
>>> da = {"a": 5, "b": 6, "c": 7, "d": 5} >>> db = dict() >>> db["a"] = 2 >>> db["b"] = 3 >>> print(db) {'a': 2, 'b': 3} >>> da["a"] + db["a"] 7
Значения словаря могут повторяться, ключи – нет.
Метод keys() возвращает не-совсем-список ключей словаря:
>>> db = dict() >>> db["a"] = 2 >>> db["b"] = 3 >>> db.keys() dict_keys(['a', 'b'])
Этот не-совсем-список можно использовать в цикле for. Но если Вам действительно нужен список (list), то надо его создать напрямую (хотя это очень редко нужно):
>>> list(db.keys()) ['a', 'b']
Ключом словаря может быть любое значение хэшируемого типа (в первом приближении – неизменяемое). К неизменяемым относятся типы int, float, bool, str, к изменяемым – list и сам dict. То есть словарь не может быть ключом словаря. Но словарь вполне может быть значением словаря, то есть ситуация вида x["a"]["b"] вполне допустима.
Проверка наличия значения в словаре
Если вы пытаетесь получить значение по ключу, которого нет в словаре, то вы получаете ошибку. Есть несколько способов избежать этого. Первый — проверить, а есть ли ключ в словаре.
Неправильный способ проверки — получить все ключи словаря методом keys и проверить наличие в них запрашиваемого. Специалисты считают этот способ крайне нежелательным.
Правильная проверка — для словарей, списков и упоминаемых далее кортежей и множеств есть конструкция "elem in <имя переменной>".
>>> db = {} # another way to create dict >>> db['a'] = 5 >>> 'a' in db True >>> 'b' in db False
Кроме in есть также not in, проверяющий отсутствие ключа в словаре.
Получение значения по умолчанию (дополнительная информация)
Иногда нам хочется получить значение для элемента в словаре, а если элемента в словаре нет, просто вернуть какое-нибудь заранее заданное значение. Для этого у словаря определен метод get, принимающий два параметра: возможный ключ и значение, возварщаемое, если ключа нет.
>>> db = {"a": 4, "b": 5} >>> db.get("a", 0) 4 >>> db.get("c", 0) 0 >>> db["d"] = db.get("d", 0) + 1 # get value for key "d", increase it by one, if key is not in dictionary, assign 1
Проход по ключам словаря
Есть несколько способов прохода по словарю.
Первое, что приходит в голову
С этим методом могли возникнуть большие проблемы в Python2.7, в Python3 это работает нормально. Но можно проще.
Рекомендуемые способы
Преимущества словарей
Получение значения по ключу и добавление нового ключа в словарь происходит значительно быстрее, чем если бы Вам пришлось перебирать список в поисках нужного значения.
Вследствие этого есть огромный набор задач, где словари использовать можно и нужно.
Вместе со списками — возможность представить структуру данных любой сложности (дополнительная информация — json).
Недостатки
Словари "кушают" много памяти.
Все операции на словарях быстры в среднем — отдельная операция может длиться очень долго.
Совсем дополнительная информация про словари
В модуле collections есть несколько модификаций стандартного словаря, позволяющих делать некоторые действия значительно проще (к примеру, подсчет разных элементов в списке)
Ну и тем, кто интересуется, как все устроено со словарем — https://ru.wikipedia.org/wiki/Хеш-таблица
Кортежи
Если очень хочется сделать ключом словаря список, то нужно использовать не list, а другой тип данных — кортеж (tuple). Кортеж (на первый взгляд) отличается от списка только заменой квадратных скобок на круглые, например:
>>> ta = (1, 2, 5, 4) >>> ta[0] 1 >>> ta[3] 4 >>> len(ta) 4
Но есть и различия, главное из которых – неизменяемость кортежа:
>>> la = [1, 2, 3, 4] >>> la[0] = 7 >>> la [7, 2, 3, 4] >>> ta = (1, 2, 3, 4) >>> ta[0] = 7 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment
Кортеж неизменяем и потому может быть ключом словаря.
Кроме того неплохо понимать, что операции доступа (получить элемент кортежа, срез и т.д.) выполняются для кортежа быстрее. Python может оптимизировать работу программы, в которой создается множество кортежей малой длины, что для списков недоступно. С другой стороны, любая операция редактирования кортежа приводит к созданию нового кортежа, что в больших количествах может замедлить работу программы и съесть память.
Множества
В некоторых случаях удобно пользоваться типом данных "множество" (set). Это именно множество, то есть его элементы не упорядочены и не могут повторяться (в отличие от списка и кортежа). На множествах определены операции объединения, пересечения и разности:
>>> s1 = set([1, 3, 5]) >>> s2 = {"a", "b", 1} >>> s1 & s2 set([1]) >>> s1 | s2 set(['a', 1, 3, 5, 'b']) >>> s1 - s2 set([3, 5])
Множества можно сравнивать:
>>> s1 < s2 False >>> s1 < set([1, 3, 5, 7]) True
Множество — изменяемый тип (не может быть ключом словаря, но может быть значением словаря, что можно применить в задании 6). Изменяемость нужна, чтобы использовать удобные в некоторых ситуациях методы add, pop и remove (см. help(set.add) и т.п.).
Как и у списка (tuple вместо list) у множества есть неизменяемый вариант: frozenset. (Для самостоятельного изучения: разберитесь, какого типа получается результат, если операцию пересечения или объединения примерить к паре значений, одно из которых — типа set, а другое — frozenset).
PEP8
Создатели языка (и Google) имеют строгие рекомендации к написанию кода — как лучше писать его, какие действия приемлемы, а какие — нет. Причем это немножко разные правила
Пожелания создателей Python изложены в PEP8: https://www.python.org/dev/peps/pep-0008/
Основные требования к коду, которые вам нужно знать на данный момент (остальные узнаете от тестирующего сервиса).
Doc-string в начале программы
Программа на Python должна начинаться с комментария, объясняющего, что она делает. Желательно, чтобы в комментарии была информация о том, как эту программу запускать. Строго говоря, это не комментарий, а строковый литерал (используются не # а кавычки, правда специальные тройные), смотрите подробнее по ссылке сверху.
Названия переменных
Не стоит называть переменные, кроме индексов, одной буквой. Это мешает пониманию кода и скорее всего приведет к ошибкам.
Переменные типа cat1, cat2, cat3 и т.д хороши только в обучающих примерах.
Названия переменных должно содержать только маленькие буквы, цифры и символ подчеркивания "_" . Например, great_job — правильное название, GreatJob — неправильное название для переменной.
Названия констант (типа числа pi), должно писать большими заглавными буквами и _. THIS_VERY_IMPORTANT_CONSTANT
Все объявленные переменные должны использоваться. Вместо переменных, которые не используются, но их приходится писать, надо ставить "пустую переменную" '_'
Отступы
По PEP8 в качестве отступов надо использовать 4 пробела. Можно настроить ваш редактор так, чтобы он автоматически вставлял вместо нажатого таба 4 пробела.
Пустые строчки
Между участками кода допустима одна пустая линия, предназначенная для разделения смысловых частей программы. Между user-defined функциями, которые будут разобраны на следующим занятии, необходимо ставить две пустые строки.
Присваивание переменным значений другого типа
Так делать в Python можно технически, но чаще всего это неоправданно и вызывает ошибки, потому будет штрафоваться. То есть не стоит писать, например:
my_line = my_line.split()
чтобы "превратить строку в список"; для хранения списка надо завести другую переменную.
Проверка, является ли строка/список/словарь пустым
Многие уже поняли, что функция len возвращает длину объекта. В связи с этим возникает искушение написать что-то типа
Однако вызов функции len оправдан только тогда, когда вам нужна именно длина, а не проверка на пустоту (например, нужно проверить, что в вашем списке меньше пяти элементов). Для тех же случаев, когда нужна проверка объекта на пустоту, достаточно написать:
или
Дело в том, что в Python списки, словари, строки и некоторые другие объекты при их наличии в условии автоматически конвертируются в False, если они пустые, и в True, если они непустые.
Кроме просто вопроса эстетики, использование такого синтаксиса работает быстрее. Подробнее — здесь: https://stackoverflow.com/questions/43121340/why-is-the-use-of-lensequence-in-condition-values-considered-incorrect-by-pyli