Come aprire un file usando l'istruzione open with


200

Sto osservando come eseguire l'input e l'output dei file in Python. Ho scritto il seguente codice per leggere un elenco di nomi (uno per riga) da un file in un altro file, controllando un nome rispetto ai nomi nel file e aggiungendo il testo alle occorrenze nel file. Il codice funziona. Potrebbe essere fatto meglio?

Avrei voluto usare l' with open(...istruzione sia per i file di input che di output, ma non riesco a vedere come potrebbero trovarsi nello stesso blocco, il che significa che dovrei archiviare i nomi in una posizione temporanea.

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

"significa che avrei bisogno di memorizzare i nomi in una posizione temporanea"? Puoi spiegarci cosa intendi con questo?
S. Lott

4
Nota che filter()è una funzione integrata e quindi probabilmente dovresti scegliere un nome diverso per la tua funzione.
Tom,

2
@Tom una funzione nello spazio dei nomi ha la precedenza sulla funzione integrata?
UpTide

2
@UpTide: Sì, Python funziona in ordine LEGB: locale, chiuso, globale, integrato (vedi stackoverflow.com/questions/291978/… ). Quindi, se crei una funzione globale ( filter()), verrà trovata prima del built-infilter()
Tom

Risposte:


308

Python consente di inserire più open()istruzioni in una sola with. Li separi virgola. Il tuo codice sarebbe quindi:

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 no, non ottieni nulla mettendo un esplicito returnalla fine della tua funzione. Puoi usarlo returnper uscire presto, ma alla fine l'hai avuto e la funzione uscirà senza di essa. (Naturalmente con le funzioni che restituiscono un valore, si utilizza il returnper specificare il valore da restituire.)

L'uso di più open()elementi con withnon era supportato in Python 2.5 quando è withstata introdotta l' istruzione, o in Python 2.6, ma è supportato in Python 2.7 e Python 3.1 o versioni successive.

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 stai scrivendo codice che deve essere eseguito in Python 2.5, 2.6 o 3.0, annida le withistruzioni come suggerito o utilizzato dalle altre risposte contextlib.nested.


28

Usa blocchi nidificati come questo,

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

12

Puoi nidificare con blocchi. Come questo:

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)

È meglio della tua versione perché garantisci che outfileverrà chiuso anche se il tuo codice incontra delle eccezioni. Ovviamente potresti farlo con try / finalmente, ma withè il modo giusto per farlo.

Oppure, come ho appena appreso, puoi avere più gestori di contesto in una dichiarazione with come descritto da @steveha . Questa mi sembra un'opzione migliore della nidificazione.

E per la tua ultima domanda minore, il ritorno non ha alcun reale scopo. Lo rimuoverei.


Grazie mille. Lo proverò e accetterò la tua risposta se / quando riuscirò a farlo funzionare.
Disnami,

Grazie ancora. Devo aspettare sette minuti prima di poter accettare.
Disnami,

7
@Disnami assicurati di accettare la risposta giusta (e non è questa!) ;-)
David Heffernan,

1

A volte, potresti voler aprire una quantità variabile di file e trattarli tutti allo stesso modo, puoi farlo con 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())                   
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.