Как открыть файл с помощью оператора open with

201

Я смотрю, как сделать файл ввода и вывода в Python. Я написал следующий код, чтобы прочитать список имен (по одному на строку) из файла в другой файл, проверяя имя по именам в файле и добавляя текст к вхождениям в файле. Код работает. Можно ли сделать это лучше?

Я хотел бы использовать with open(...оператор как для входных, так и для выходных файлов, но не вижу, как они могут быть в одном и том же блоке, то есть мне нужно хранить имена во временном местоположении.

def filter(txt, oldfile, newfile):
    '''\
    Read a list of names from a file line by line into an output file.
    If a line begins with a particular name, insert a string of text
    after the name before appending the line to the output file.
    '''

    outfile = open(newfile, 'w')
    with open(oldfile, 'r', encoding='utf-8') as infile:
        for line in infile:
            if line.startswith(txt):
                line = line[0:len(txt)] + ' - Truly a great person!\n'
            outfile.write(line)

    outfile.close()
    return # Do I gain anything by including this?

# input the name you want to check against
text = input('Please enter the name of a great person: ')    
letsgo = filter(text,'Spanish', 'Spanish2')
Disnami
источник
«Значит, мне нужно хранить имена во временном месте»? Можете ли вы объяснить, что вы подразумеваете под этим?
S.Lott
4
Обратите внимание , что filter()является встроенной функцией , и поэтому вы , вероятно , следует выбрать другое имя для функции.
Том
2
@Tom ли функция в пространстве имен переопределяет встроенную функцию?
UpTide
2
@UpTide: Да, Python работает в порядке LEGB - локальный, закрытый, глобальный, встроенный (см. Stackoverflow.com/questions/291978/… ). Итак, если вы сделаете глобальную функцию ( filter()), она будет найдена до встроеннойfilter()
Tom

Ответы:

310

Python позволяет поместить несколько open()операторов в один with. Вы разделяете их запятыми. Ваш код будет тогда:

def filter(txt, oldfile, newfile):
    '''\
    Read a list of names from a file line by line into an output file.
    If a line begins with a particular name, insert a string of text
    after the name before appending the line to the output file.
    '''

    with open(newfile, 'w') as outfile, open(oldfile, 'r', encoding='utf-8') as infile:
        for line in infile:
            if line.startswith(txt):
                line = line[0:len(txt)] + ' - Truly a great person!\n'
            outfile.write(line)

# input the name you want to check against
text = input('Please enter the name of a great person: ')    
letsgo = filter(text,'Spanish', 'Spanish2')

И нет, вы ничего не получите, поставив явное returnв конце своей функции. Вы можете использовать, returnчтобы выйти рано, но у вас было это в конце, и функция выйдет без него. (Конечно, с функциями, которые возвращают значение, вы используете returnдля указания возвращаемого значения.)

Использование нескольких open()предметов сwith не поддерживалось в Python 2.5, когда withвводился оператор, или в Python 2.6, но оно поддерживается в Python 2.7 и Python 3.1 или новее.

http://docs.python.org/reference/compound_stmts.html#the-with-statement http://docs.python.org/release/3.1/reference/compound_stmts.html#the-with-statement

Если вы пишете код, который должен работать на Python 2.5, 2.6 или 3.0, вложите withоператоры в соответствии с другими предложенными или используемыми ответами contextlib.nested.

steveha
источник
29

Используйте вложенные блоки, как это,

with open(newfile, 'w') as outfile:
    with open(oldfile, 'r', encoding='utf-8') as infile:
        # your logic goes right here
RanRag
источник
12

Вы можете вложить свои блоки. Как это:

with open(newfile, 'w') as outfile:
    with open(oldfile, 'r', encoding='utf-8') as infile:
        for line in infile:
            if line.startswith(txt):
                line = line[0:len(txt)] + ' - Truly a great person!\n'
            outfile.write(line)

Это лучше, чем ваша версия, потому что вы гарантируете, что outfileона будет закрыта, даже если ваш код встречает исключения. Очевидно, что вы можете сделать это с помощью try / finally, но withэто правильный способ сделать это.

Или, как я только что узнал, вы можете иметь несколько контекстных менеджеров в операторе with, как описано в @steveha . Мне кажется, это лучший вариант, чем вложение.

И для вашего последнего незначительного вопроса, возвращение не имеет смысла. Я бы удалил это.

Дэвид Хеффернан
источник
Огромное спасибо. Я попробую и приму ваш ответ, если / когда я заставлю его работать.
Диснами
Еще раз спасибо. Нужно подождать семь минут, прежде чем я смогу принять.
Диснами
7
@Disnami убедитесь, что вы принимаете правильный ответ (и не этот!) ;-)
Дэвид Хеффернан
1

Иногда вам может понадобиться открыть переменное количество файлов и обращаться с ними одинаково, вы можете сделать это с contextlib

from contextlib import ExitStack
filenames = [file1.txt, file2.txt, file3.txt]

with open('outfile.txt', 'a') as outfile:
    with ExitStack() as stack:
        file_pointers = [stack.enter_context(open(file, 'r')) for file in filenames]                
            for fp in file_pointers:
                outfile.write(fp.read())                   
брат-било
источник