Учебная страница курса биоинформатики,
год поступления 2017
Советы и подсказки к заданию 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/2/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 четвертый способ форматирования вывода — https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep498 )
Запись информации в файл
Чтобы писать в файл, его нужно открыть на запись командой
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() ['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
Проход по ключам словаря
Есть несколько способов прохода по словарю.
Неправильный способ — да обрушится кара Господня на того, кто так делает для большого словаря
Рекомендуемые способы
или
Преимущества словарей
Получение значения по ключу и добавление нового ключа в словарь происходит значительно быстрее, чем если бы вам пришлось перебирать список в поисках нужного значения.
Вследствие этого есть огромный набор задач, где словари использовать можно и нужно.
Вместе со списками — возможность представить структуру данных любой сложности (дополнительная информация — 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) имеют строгие рекомендации к написанию кода — как лучше писать его, какие действия приемлемы, а какие — нет.
Все это изложено в 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