Работа с файлами -- неотделимая часть жизни биоинформатика. До 90% работы среднестатистического биоинформатика уходит на парсинг файлов и их конвертацию между форматами.
Что такое файл?
Файл - это данные, которые хранятся на некотором носителе, например, на жестком диске.
Как открыть файл для чтения?
Команда open(filename, "r")
открывает ваш файл для чтения.
myfile = open('spring1.txt', "r")
myfile = open('spring1.txt', "r")
Объект myfile
имеет интересный тип (название его мало о чем говорит, достаточно понимать, что такой тип объектов позволяет читать из себя данные).
type(myfile)
_io.TextIOWrapper
Как прочитать весь файл?
Можно прочитать весь файл разом методом .read
print(myfile.read())
Бесконечно можно смотреть на три вещи: на горящий огонь, на бегущую воду и на собственный код месячной давности.
Что необходимо сделать после работы с файлом?
Открытый файл после окончания работы НАДО закрыть командной close
myfile.close()
Как ещё можно читать из файла?
Из файла также можно читать построчно:
myfile2 = open("spring2.txt", "r")
print(myfile2.readline())
print(myfile2.readline())
myfile2.close()
- Мне сказали: умный человек! - Ну мало ли, что про человека болтают.
\n
на конце.¶Как можно убрать \n
в конце строки?
Метод .strip
строки поможет нам!
myfile2 = open("spring2.txt", "r")
print(myfile2.readline().strip())
print(myfile2.readline().strip())
- Мне сказали: умный человек! - Ну мало ли, что про человека болтают.
А что произойдёт, если прочитать строку после того, как мы дошли до конца файла?
Когда файл дочитан до конца, метод .readline
возвращает пустую строку (без \n
на конце)
print("Returning empty string : |", myfile2.readline(), "|", sep="")
myfile2.close()
Returning empty string : ||
Казалось бы, можно организовать чтение всего файла следующим образом:
myfile3 = open("spring3.txt", "r")
line = myfile3.readline()
while line != "":
print(line.strip())
line = myfile3.readline() #!
myfile3.close()
Ученые подсчитали, что шансы реального существования столь откровенно абсурдного мира равняются одному на миллион. Однако волшебники подсчитали, что шанс «один на миллион» выпадает в девяти случаях из десяти.
Почему не используем .strip
сразу в строке, помеченной #!
?
Однако в Python есть механизм, который помогает упростить код, написанный выше.
Для чтения файла можно использовать цикл for
.
Если же нужно обработать все строки из файла одинаковым способом, то этот будет наиболее предпочтителен - он не только компактнее и соответствует стилю Python, но еще и работает быстрее.
myfile4 = open("spring4.txt", "r")
for line in myfile4:
print(line.strip())
myfile4.close()
Если разобраться, — сказал Ливитт, — так это проблема из проблем. Как дезинфицировать человеческое тело, грязней которого, наверно, нет ничего во всей Вселенной, не уморив при этом человека?
Как можно догадаться, итерация идет по строкам!
Пройдя по файлу один раз, вы попадаете в конец файла.
Это означает, что вы не сможете пройтись по нему еще раз!
tabular = open("tab.txt", "r")
for line in tabular:
print(line.strip())
for line in tabular: #won't be executed
print("WOW!", line.strip())
tabular.close()
#name seqlen class mouse 100 + human 120 - cat 140 +
Аналогично, если из файла уже что-то читалось - второй раз оно прочитано не будет
infile = open("tab.txt", "r")
print("\t\tReading the first line")
print(infile.readline().strip())
print("\t\tReading by 'for' cycle")
for line in infile:
print(line.strip())
infile.close()
Reading the first line #name seqlen class Reading by 'for' cycle mouse 100 + human 120 - cat 140 +
Что с этим поделать?
Можно переместиться в начало файла с помощью метода .seek
.
infile = open("tab.txt", "r")
for line in infile:
print(line.strip())
print("\nReading file from the beginning\n")
infile.seek(0)
for line in infile:
print(line.strip())
infile.close()
#name seqlen class mouse 100 + human 120 - cat 140 + Reading file from the beginning #name seqlen class mouse 100 + human 120 - cat 140 +
Как открыть файл на запись?
Открытие файла на запись - open(filename, "w")
(эта команда создает новый пустой файл)
Запись в файл осуществляется методом .write
munch = open("Munchhausen.txt", "w")
phrase = """В свое время Сократ мне сказал:
"Женись непременно.
Попадется хорошая жена — станешь счастливым.
Плохая — станешь философом."
Не знаю, что лучше\n"""
munch.write(phrase)
munch.close()
Обратите внимание: метод .write
не добавляет \n
в конце строки!
Вам нужно добавлять перенос строки самостоятельно :^)
Проверим нашу запись:
munch = open("Munchhausen.txt", "r")
print(munch.read())
munch.close()
В свое время Сократ мне сказал: "Женись непременно. Попадется хорошая жена — станешь счастливым. Плохая — станешь философом." Не знаю, что лучше
Можно также записать информацию в конец уже существующего файла, открыв его в режиме "a"
- от "append".
f = open("Munchhausen.txt", "a")
s = """После свадьбы мы сразу уехали в свадебное путешествие.
Я в Турцию, жена в Швейцарию,
и прожили там три года в любви и согласии.\n"""
f.write("\n")
f.write(s)
f.write("@Тот самый Мюнхгаузен\n")
f.close()
f = open("Munchhausen.txt", 'r')
print(f.read())
f.close()
В свое время Сократ мне сказал: "Женись непременно. Попадется хорошая жена — станешь счастливым. Плохая — станешь философом." Не знаю, что лучше После свадьбы мы сразу уехали в свадебное путешествие. Я в Турцию, жена в Швейцарию, и прожили там три года в любви и согласии. @Тот самый Мюнхгаузен
Все открытые файлы автоматически закрываются, когда программа заканчивает работу.
И потому все будет прекрасно работать, пока вы не решите создавать в своей программе промежуточные файлы, информация из которых затем используется в ней же.
cat = "cat.txt"
catfile = open(cat, "w")
catfile.write("Hello, ")
catfile.write("world\n")
catread = open(cat, "r")
print(catread.read())
Внезапно! Вы что-то записали в файл, не закрыв его, а этого там не оказалось => ваша программа не нашла то, что искала, и упала.
Почему так?
Дело в том, что операции чтения/записи с жесткого диска не являются самыми быстрыми в мире. Потому система предпочитает записывать на жесткий диск не каждый раз, когда вы указали ей это сделать, а ждать, пока не наберется достаточно большой кусок данных. Потому в незакрытый файл вполне возможно запись произведена до конца работы программы и не будет.
А вот если его все же закрыть, то:
catfile.close()
print(catread.read())
catread.close()
Hello, world
Более того, время от времени [даже] в биоинформатике вам приходится писать программы, работающие долгое время, возможно, отвечающие на тысячи задач.
Если вы забываете закрывать файлы, открытые разными запросами, то в какой-то момент вы просто не сможете открыть новый файл и ваша программа завершится аварийно.
По-умному такая ситуация называется "утечка файловых дескрипторов".
Демонстрация того, что будет, если открыть слишком много файлов (это верно как для чтения, так и для записи):
opened_files = []
for i in range(1, 10000):
fl = open(str(i), "w")
opened_files.append(fl)
--------------------------------------------------------------------------- OSError Traceback (most recent call last) <ipython-input-18-5f9900e3f484> in <module> 2 3 for i in range(1, 10000): ----> 4 fl = open(str(i), "w") 5 opened_files.append(fl) OSError: [Errno 24] Too many open files: '962'
К счастью, разработчики Python не дураки :^)
Существует способ автоматически закрывать файл, даже если в процессе у вас возникли ошибки, - о нем мы поговорим чуть позже.
В работе с Python мы часто сталкивались с исключительными ситуациями, в которых мы получали ошибки - исключения. Необработанное исключение приходит к завершению Python-программы
Пример хорошей обработки исключений
Медведь убежал из зоопарка, не могут найти.
Наконец звонит бабуля:
— Ой, ко мне во двор медведь забрался и на дерево залез! Уберите его, мне страшно!
Приезжает мужик с маленькой собачкой, даёт бабуле ружье, показывает на собачку и говорит:
— Бабуля, это Кефирчик. Я сейчас залезу наверх, встряхну ветку, медведь свалится, Кефирчик схватит его за ляжку и потащит в зоопарк.
— Хорошо, сынок. А зачем же мне ружье?
— Если вместо медведя свалюсь я, стреляй в Кефирчика!
Супер. А как ловить исключения?
В Python существует специальный механизм для отлова исключений:
try:
pass
except:
pass
1 / 0
--------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) <ipython-input-22-b971f1464605> in <module> ----> 1 1 / 0 ZeroDivisionError: division by zero
try:
1 / 0
except ZeroDivisionError:
print("Surprise, ьщерукагслук")
Surprise, ьщерукагслук
Порой нам необходимо отловить несколько исключений. В этом случае просто пишем несколько блоков except
a = 10
b = 5
try:
a = int(a) # probable error
b = int(b) # probable error
print(a / b) # probable error
except ValueError:
print("a and b must be integers")
except ZeroDivisionError:
print("b must be different from 0")
2.0
Порой нам необходимо отловить несколько исключений. В этом случае просто пишем несколько блоков except
a = "1"
b = "6"
try:
a = int(a) # probable error
b = int(b) # probable error
print(a / b) # probable error
except ValueError:
print("a and b must be integers")
except ZeroDivisionError:
print("b must be different from 0")
0.16666666666666666
Порой нам необходимо отловить несколько исключений. В этом случае просто пишем несколько блоков except
a = "MCMXXCIV"
b = "MCDLXXXVIII"
try:
a = int(a) # probable error
b = int(b) # probable error
print(a / b) # probable error
except ValueError:
print("a and b must be integers")
except ZeroDivisionError:
print("b must be different from 0")
a and b must be integers
Порой нам необходимо отловить несколько исключений. В этом случае просто пишем несколько блоков except
a = 2
b = 0
try:
a = int(a) # probable error
b = int(b) # probable error
print(a / b) # probable error
except ValueError:
print ("a and b must be integers")
except ZeroDivisionError:
print ("b must be different from 0")
b must be different from 0
Может возникнуть ситуация, когда мы хотим отлавливать исключения, которые являются подтипами друг друга. В этом случае необходимо указывать наиболее частное исключение первым.
Такой код не будет работать так, как мы хотим:
a = 12
b = 0
try:
a = int(a) # probable error
b = int(b) # probable error
print(a / b) # probable error
print(a[0]) # probable error
except Exception:
print("Other exception")
except ValueError:
print("a and b must be integers")
except ZeroDivisionError:
print("b must be different from 0")
Other exception
В каких условиях и почему код не будет работать корректно?
Теперь код работает верно!
a = 12
b = 0
try:
a = int(a) # probable error
b = int(b) # probable error
print(a / b) # probable error
print(a[0]) # probable error
except ValueError:
print("a and b must be integers")
except ZeroDivisionError:
print("b must be different from 0")
except Exception:
print("Other exception")
b must be different from 0
Как описать ситуацию, когда мы хотим что-то сделать только в случае, если исключения не произошло?
a = 23
b = 0
try:
a = int(a) # probable error
b = int(b) # probable error
result = a / b # probable error
except ValueError:
print("a and b must be integers")
except ZeroDivisionError:
print("b must be different from 0")
except Exception:
print("Other exception")
print(result)
b must be different from 0
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-30-900bc769499f> in <module> 12 print("Other exception") 13 ---> 14 print(result) NameError: name 'result' is not defined
else
¶Существуют еще ветвь else
в try/except
, которая выполняется только в том случае, если исключения не произошло.
a = 14
b = 0
try:
a = int(a) # probable error
b = int(b) # probable error
result = a / b # probable error
except ValueError:
print("a and b must be integers")
except ZeroDivisionError:
print("b must be different from 0")
except Exception:
print("Other exception")
else:
print(result)
b must be different from 0
finally
¶И последним оператором, который можно использовать в try/except
, является finally
.
Под ним перечисляются действия, которые должны быть совершены в любом случае, даже если мы не обрабатывали ошибки!
a = "XVII"
b = "LXI"
try:
a = int(a) # probable error
b = int(b) # probable error
result = a / b # probable error
print(result[0]) # probable error
except ValueError:
print("a and b must be integers")
except ZeroDivisionError:
print("b must be different from 0")
else:
print(result)
finally:
print("...Well, at least we got there...")
a and b must be integers ...Well, at least we got there...
Обычно в finally
освобождаются ресурсы, которые мы брали в блоке try
и хотим несмотря ни на что освободить. Например, если бы мы не имели блока with
для файла, то вынуждать его к закрытию пришлось бы следующим образом:
a = 10
b = 0
try:
fl = open("outfile.txt", "w")
a = int(a) # probable error
b = int(b) # probable error
result = a / b # probable error
fl.write(f"{result}\n")
except ValueError:
print("a and b must be integers")
except ZeroDivisionError:
print("b must be different from 0")
else:
print(result)
finally:
print("Closing file")
fl.close()
b must be different from 0 Closing file
with
(менеджер контекста)¶Казалось бы, почему реже используется последняя конструкция?
Ведь нам удобно иметь возможность закрыть открытый файл, даже если в нашем коде работы с ним произошла ошибка.
На самом деле, в Python есть специальная конструкция для этого - with
Эта конструкция использует finally
.
in_file = open("spring5.txt", "r")
try:
print(in_file.read())
a = 5 / 0
finally:
in_file.close()
Когда делаешь шаг с обрыва, жизнь моментально принимает очень чёткое направление.
--------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) <ipython-input-34-dc9d202118b4> in <module> 2 try: 3 print(in_file.read()) ----> 4 a = 5 / 0 5 finally: 6 in_file.close() ZeroDivisionError: division by zero
print(in_file.closed)
True
with open("spring5.txt", "r") as in_file2:
print(in_file2.read())
a = 5 / 0
Когда делаешь шаг с обрыва, жизнь моментально принимает очень чёткое направление.
--------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) <ipython-input-36-a7d94ac30f7e> in <module> 1 with open("spring5.txt", "r") as in_file2: 2 print (in_file2.read()) ----> 3 a = 5 / 0 ZeroDivisionError: division by zero
print(in_file2.closed)
True
assert
¶assert
позволяет проверить условие и вызвать ошибку, если условие не выполняется:
assert 10 == 10
assert 10 == 2
--------------------------------------------------------------------------- AssertionError Traceback (most recent call last) <ipython-input-39-ed8f5ab137f6> in <module> ----> 1 assert 10 == 2 AssertionError:
v1 = 10
v2 = 0
assert v2 != 0, 'You passed wrong denominator'
--------------------------------------------------------------------------- AssertionError Traceback (most recent call last) <ipython-input-40-b3e05dc76c09> in <module> 1 v1 = 10 2 v2 = 0 ----> 3 assert v2 != 0, 'You passed wrong denominator' AssertionError: You passed wrong denominator
Многие используют assert
при поиске ошибок в своей программе, что (вообще-то говоря) неверно, но всех это не очень задевает
В работе с данными удобно использовать информацию, записанную в таблицы.
Чаще всего используется .csv
-- Comma-Separated Values и .tsv
-- Tab Separated Values форматы.
import csv
petal_widths = []
with open('example.csv') as csvfile:
iris_dataset = csv.reader(csvfile)
header = next(iris_dataset)
for row in iris_dataset:
petal_w = float(row[2])
petal_widths.append(petal_w)
print(sum(petal_widths) / len(petal_widths))
3.7586666666666693
JSON
[read: jeyson]¶(JavaScript Object Notation, see https://www.json.org/ )
Создадим словарь и запишем его в json
-файл:
d = {"name": "John", "age": 20, "os": "linux"}
import json
with open('example.json', 'w') as f:
json.dump(d, f)
%%bash
less example.json
{"name": "John", "age": 20, "os": "linux"}
Теперь прочтём данные из другого файла!
with open('complex.json', 'r') as f:
d = json.load(f)
d
{'maps': [{'id': 'blabla', 'iscategorical': '0'}, {'id': 'blabla', 'iscategorical': '0'}], 'masks': {'id': 'valore'}, 'om_points': 'value', 'parameters': {'id': 'valore'}}
d = json.load(open('complex.json', 'r'))
json.loads(open('example.json').read())
{'name': 'John', 'age': 20, 'os': 'linux'}
Формат имеет достаточно жесткий синтаксис:
json.loads(open('example.json').read() + ']')
--------------------------------------------------------------------------- JSONDecodeError Traceback (most recent call last) <ipython-input-48-5ff0619d9ae3> in <module> ----> 1 json.loads(open('example.json').read() + ']') /usr/lib/python3.6/json/__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw) 352 parse_int is None and parse_float is None and 353 parse_constant is None and object_pairs_hook is None and not kw): --> 354 return _default_decoder.decode(s) 355 if cls is None: 356 cls = JSONDecoder /usr/lib/python3.6/json/decoder.py in decode(self, s, _w) 340 end = _w(s, end).end() 341 if end != len(s): --> 342 raise JSONDecodeError("Extra data", s, end) 343 return obj 344 JSONDecodeError: Extra data: line 1 column 43 (char 42)
pickle
¶Бинарные файлы меньше текстовых, их быстрее считывать и проще хранить.
import pickle
d = {"name": "John", "age": 20, "os": "linux"}
with open("example.pickle", "wb") as pickle_out:
pickle.dump(d, pickle_out)
with open("example.pickle", "rb") as pickle_in:
d2 = pickle.load(pickle_in)
d2
{'name': 'John', 'age': 20, 'os': 'linux'}
Больше информации: https://docs.python.org/3/howto/urllib2.html
import urllib.request
with urllib.request.urlopen('http://python.org/') as response:
html = response.read()
with urllib.request.urlopen('http://humanstxt.org/humans.txt') as response:
txt = response.readlines()
txt[:5]
[b'\xef\xbb\xbf/* TEAM */\n', b'\tChef:Juanjo Bernabeu\n', b'\tContact: hello [at] humanstxt.org\n', b'\tTwitter: @juanjobernabeu\n', b'\tFrom:Barcelona, Catalonia, Spain\n']
print(txt[2])
print(txt[2].decode())
b'\tContact: hello [at] humanstxt.org\n' Contact: hello [at] humanstxt.org
Есть расширение? Да (.bed, .fa, .pdb) -> Google: bed/fa/pdb/... format specification
Расширения нет. Читается ли файл как текстовый? Да -> Ищем информацию о формате или намеки в заголовке.
Файл не читается как текстовый. Despair.
Если формат всё-таки определен, не стоит сразу же бросаться писать его парсер, этот велосипед может быть уже избретен. Иногда удобнее конвертировать в другой формат и уже его читать.
Спецификации файлов, часто используемых для геномных данных: https://genome.ucsc.edu/FAQ/FAQformat.html Иногда спецификация хорошо описана на Википедии (https://en.wikipedia.org/wiki/Protein_Data_Bank_(file_format) ), но предпочтение стоит отдавать описанию формата от самих авторов.
glob
и os
являются незаменимыми для работы с файловой системой через Python.
glob.glob
позволяет осуществлять поиск файлов по маске:
import glob
glob.glob('*.txt')
['spring4.txt', 'cat.txt', 'dummy_text.txt', 'tab.txt', 'outfile.txt', 'spring1.txt', 'example_seqs.txt', 'infile.txt', 'spring3.txt', 'spring2.txt', 'spring5.txt', 'Munchhausen.txt', 'new_file.txt']
Модуль os
позволяет работать с файловой системой одинаково, вне зависимости от платформы (Windows, Linux и т.д.)
import os
os.path.isfile('dummy_text.txt')
True
os.path.isdir('dummy_text.txt')
False
os.mkdir('tmp/')
Полезный способ правильно собрать путь по частям.
Способ не зависит от того, как пользователь ввел имя директории (с /
или без), например.
os.path.join("Vasya", "hw72", "exercise89")
'Vasya/hw72/exercise89'
os.path.join("Vasya/", "hw72/", "exercise89")
'Vasya/hw72/exercise89'
os.path.join("Vasya/", "hw72", "exercise89")
'Vasya/hw72/exercise89'