Como abrir um arquivo usando a instrução open with


200

Estou vendo como fazer entrada e saída de arquivos em Python. Escrevi o código a seguir para ler uma lista de nomes (um por linha) de um arquivo para outro arquivo, verificando um nome com relação aos nomes no arquivo e anexando texto às ocorrências no arquivo. O código funciona. Poderia ser feito melhor?

Eu queria usar a with open(...instrução para os arquivos de entrada e saída, mas não consigo ver como eles podem estar no mesmo bloco, o que significa que eu precisaria armazenar os nomes em um local temporário.

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')

"significando que eu precisaria armazenar os nomes em um local temporário"? Você pode explicar o que você quer dizer com isso?
31512 S.Lott

4
Observe que filter()é uma função interna e, portanto, você provavelmente deve escolher um nome diferente para sua função.
Tom

2
@ Tom uma função no espaço para nome substitui a função interna?
UpTide

2
@UpTide: Sim, o Python opera em ordem LEGB - Local, Anexo, Global, Integrado (consulte stackoverflow.com/questions/291978/… ). Portanto, se você criar uma função global ( filter()), ela será encontrada antes da incorporada.filter()
Tom

Respostas:


308

Python permite colocar várias open()instruções em um único with. Você os separa por vírgula. Seu código seria então:

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')

E não, você não ganha nada colocando um explícito returnno final de sua função. Você pode usar returnpara sair mais cedo, mas você o teve no final, e a função sairá sem ele. (Obviamente, com funções que retornam um valor, use o returnpara especificar o valor a ser retornado.)

O uso de vários open()itens com withnão era suportado no Python 2.5 quando a withinstrução foi introduzida ou no Python 2.6, mas é suportado no Python 2.7 e Python 3.1 ou mais recente.

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

Se você estiver escrevendo um código que deve ser executado no Python 2.5, 2.6 ou 3.0, aninhe as withinstruções como as outras respostas sugeridas ou usadas contextlib.nested.


28

Use blocos aninhados como este,

with open(newfile, 'w') as outfile:
    with open(oldfile, 'r', encoding='utf-8') as infile:
        # your logic goes right here

12

Você pode aninhar o seu com blocos. Como isso:

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)

Isso é melhor que a sua versão, porque você garante que outfileserá fechado mesmo que seu código encontre exceções. Obviamente, você poderia fazer isso com try / finalmente, mas withé o caminho certo para fazer isso.

Ou, como acabei de aprender, você pode ter vários gerenciadores de contexto em uma declaração with, conforme descrito por @steveha . Isso me parece ser uma opção melhor do que aninhar.

E para sua última pergunta menor, o retorno não serve a nenhum propósito real. Eu o removeria.


Muito obrigado. Vou experimentá-lo e aceitar sua resposta se / quando fazê-lo funcionar.
Disnami

Obrigado novamente. Preciso esperar sete minutos antes que eu possa aceitar.
Disnami

7
@Disnami Certifique-se de aceitar a resposta certa (e não é este!) ;-)
David Heffernan

1

Às vezes, convém abrir uma quantidade variável de arquivos e tratar cada um da mesma forma, você pode fazer isso com 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())                   
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.