#python
#python
Вопрос:
Я пытаюсь добавить зависимости из списка в requirements.txt файл в зависимости от платформы, на которой будет выполняться программное обеспечение. Итак, я написал следующий код:
if platform.system() == 'Windows':
# Add windows only requirements
platform_specific_req = [req1, req2]
elif platform.system() == 'Linux':
# Add linux only requirements
platform_specific_req = [req3]
with open('requirements.txt', 'a ') as file_handler:
for requirement in platform_specific_req:
already_in_file = False
# Make sure the requirement is not already in the file
for line in file_handler.readlines():
line = line.rstrip() # remove 'n' at end of line
if line == requirement:
already_in_file = True
break
if not already_in_file:
file_handler.write('{0}n'.format(requirement))
file_handler.close()
Но что происходит с этим кодом, так это то, что когда второе требование будет найдено в списке требований, уже имеющихся в файле, for line in file_handler.readlines():
кажется, что оно указывает на последний элемент списка в файле, поэтому новое требование фактически сравнивается только с последним элементом в списке, иесли это не тот же файл, он добавляется. Очевидно, что это приводит к дублированию нескольких элементов в списке, поскольку только первое требование сравнивается со всеми элементами в списке. Как я могу сказать python снова начать сравнение с верхней части файла?
Решение: я получил много отличных ответов, я многому научился, спасибо, ребята. В итоге я объединил два решения; одно от Антти Хаапалы и одно от Мэтью Фрэнглена в одно. Я показываю окончательный код здесь для справки:
# Append the extra requirements to the requirements.txt file
with open('requirements.txt', 'r') as file_in:
reqs_in_file = set([line.rstrip() for line in file_in])
missing_reqs = set(platform_specific_reqs).difference(reqs_in_file)
with open('requirements.txt', 'a') as file_out:
for req in missing_reqs:
file_out.write('{0}n'.format(req))
Комментарии:
1. почему у вас есть требования к конкретной платформе? Для чего вы контролируете? Новые строки?
2. Не повторяйте
readlines
обработчик файла сам по себе является итеративным, в то время как readlines материализует весь файл в список, тратя впустую память.
Ответ №1:
Ответ на ваш явный вопрос: file_handler.seek(0) будет искать его обратно в начало файла.
Некоторые аккуратные улучшения:
Вы можете использовать сам обработчик файла в качестве итератора вместо вызова метода readlines() .
Если ваш файл слишком велик, чтобы его можно было полностью прочитать в памяти, тогда можно выполнить итерацию по строкам в файле напрямую, но вам следует изменить способ, которым вы это делаете. Как есть, вы перебираете весь файл для каждого требования, но ввод-вывод является дорогостоящим. Вероятно, вам следует перебирать строки и для каждой строки проверять, соответствует ли это одному из требований. Вот так:
with open('requirements.txt', 'a ') as file_handler:
for line in file_handler:
line = line.rstrip()
if line in platform_specific_req:
platform_specific_req.remove(line)
for req in platform_specific_req:
file_handler.write('{0}n'.format(req))
Комментарии:
1.конечно, в этом случае файл, который, скорее всего, является
pip
requirements.txt
it, вряд ли когда-либо будет слишком большим, чтобы поместиться в памяти2. также
with
закроет файл.
Ответ №2:
Вы открываете дескриптор файла перед итерацией по существующему списку требований. Затем вы читаете весь дескриптор файла для каждого требования.
Дескриптор файла завершится после первого требования, потому что вы не открывали его повторно. Повторное открытие файла для каждой итерации было бы очень расточительным — считайте файл в список, а затем используйте его внутри циклов. Или выполните сравнение наборов!
file_content = set([line.rstrip() for line in file_handler])
only_in_platform = set(platform_specific_req).difference(file_content)
Комментарии:
1. Выполнение сравнения наборов не работает, потому что строки из файла имеют n в конце, в то время как значения в platform_specific_req не имеют этого n
2. Я обновил понимание списка, чтобы удалить новую строку и адрес комментария Аарона
Ответ №3:
Не пытайтесь повторно читать файл для каждого требования. Хотя добавление действительно работает для этого самого варианта использования, для модификаций в целом проще просто:
- Считайте содержимое из файла в список (желательно, пропуская пустые строки)
- Измените список
- Снова откройте файл для записи и сохраните измененные данные.
Так, например
with open('requirements.txt', 'r') as fin:
requirements = [ i for i in (line.strip() for line in fin) if i ]
for req in platform_specific_req:
if req not in requirements:
requirements.append(req)
with open('requirements.txt', 'w') as fout:
for req in requirements:
fout.write('{0}n'.format(req))
# or print(req, file=fout)
Комментарии:
1. Хорошо для использования диспетчера контекста, я предпочитаю
rU
флаги для универсальной поддержки перевода строки.2. Я думаю, он имеет в виду добавление, поэтому
'w'
было бы неправильно, если бы вы перезаписали файл.3. Нет, этот код делает то, что он хочет сделать, то есть добавляет недостающие требования к файлу. Цитирую: «Я пытаюсь добавить зависимости из списка в requirements.txt файл в зависимости от платформы, на которой будет выполняться программное обеспечение «. что было выполнено.
Ответ №4:
Я знаю, что отвечаю немного поздно, но я бы посоветовал сделать это таким образом, открыв его один раз, прочитав и добавив одновременно. Обратите внимание, что это должно работать на любой платформе, независимо от вашей системы:
import os
def ensure_in_file(lines, file_path):
'''
idempotent function to append lines to a file if they're not already there
'''
with open(file_path, 'r U') as f: # r U allows append, Universal Newline mode
# set of all lines in the file, less newlines, and trailing spaces too.
file_lines = set(l.rstrip() for l in f)
# write lines not in the file, add the os line separator as you go
f.writelines(l os.linesep for l in set(lines).difference(file_lines))
Вы можете проверить это
a_file = '/temp/temp/foo/bar' # insert your own file path here.
# with open(a_file, 'w') as f: # ensure a blank file
# pass
ensure_in_file(['this', 'that'], a_file)
with open(a_file, 'rU') as f:
print f.read()
ensure_in_file(['this', 'that'], a_file)
with open(a_file, 'rU') as f:
print f.read()
Каждый оператор печати должен демонстрировать, что файл содержит каждую строку один раз.