La lettura di un intero file lascia aperto l'handle del file?


372

Se leggi un intero file con content = open('Path/to/file', 'r').read()l'handle del file viene lasciato aperto fino alla chiusura dello script? Esiste un metodo più conciso per leggere un intero file?

Risposte:


585

La risposta a questa domanda dipende in qualche modo dalla particolare implementazione di Python.

Per capire di cosa si tratta, presta particolare attenzione fileall'oggetto reale . Nel tuo codice, quell'oggetto è menzionato una sola volta, in un'espressione, e diventa inaccessibile immediatamente dopo il read()ritorno della chiamata.

Ciò significa che l'oggetto file è immondizia. L'unica domanda rimasta è "Quando il garbage collector raccoglierà l'oggetto file?".

in CPython, che utilizza un contatore di riferimento, questo tipo di immondizia viene notato immediatamente e quindi verrà raccolto immediatamente. Questo non è generalmente vero per altre implementazioni di Python.

Una soluzione migliore, per assicurarsi che il file sia chiuso, è questo modello:

with open('Path/to/file', 'r') as content_file:
    content = content_file.read()

che chiuderà sempre il file immediatamente dopo la fine del blocco; anche se si verifica un'eccezione.

Modifica: per aggiungere un punto più preciso:

A parte questo file.__exit__(), che viene chiamato "automaticamente" in un'impostazione del withgestore di contesto, l'unico altro modo che file.close()viene chiamato automaticamente (ovvero, oltre a chiamarlo esplicitamente da solo) è tramite file.__del__(). Questo ci porta alla domanda su quando __del__()viene chiamato?

Un programma scritto correttamente non può presumere che i finalizzatori verranno mai eseguiti in qualsiasi momento prima della fine del programma.

- https://devblogs.microsoft.com/oldnewthing/20100809-00/?p=13203

In particolare:

Gli oggetti non vengono mai esplicitamente distrutti; tuttavia, quando diventano irraggiungibili, possono essere raccolti nella spazzatura. Un'implementazione può posticipare la garbage collection o ometterla del tutto - è una questione di qualità dell'implementazione il modo in cui la garbage collection viene implementata, purché non vengano raccolti oggetti che siano ancora raggiungibili.

[...]

CPython attualmente utilizza uno schema di conteggio dei riferimenti con il rilevamento (facoltativo) ritardato di immondizia collegata ciclicamente, che raccoglie la maggior parte degli oggetti non appena diventano irraggiungibili, ma non è garantito che raccolga immondizia contenente riferimenti circolari.

- https://docs.python.org/3.5/reference/datamodel.html#objects-values-and-types

(Enfasi mia)

ma come suggerisce, altre implementazioni possono avere altri comportamenti. Ad esempio, PyPy ha 6 diverse implementazioni di garbage collection !


24
Per un po 'non c'erano davvero altre implementazioni di Python; ma fare affidamento sui dettagli di implementazione non è proprio Pythonic.
Karl Knechtel,

È ancora specifico per l'implementazione o era già standardizzato? Non chiamare __exit__()in questi casi sembra un difetto di progettazione.
rr-

2
@jgmjgm È proprio a causa di questi 3 problemi, essendo GC imprevedibile try/ finallyfiddly e l'utile molto comune dei gestori di cleanup che withrisolve. La differenza tra "chiusura esplicita" e "gestione con with" è che il gestore di uscita viene chiamato anche se viene generata un'eccezione. Si potrebbe mettere il close()in una finallyclausola di, ma che non è molto diverso da utilizzare with, invece, un po 'Messier (3 linee extra invece di 1), e un po' più difficile per ottenere appena a destra.
SingleNegationElimination

1
Quello che non capisco è perché 'with' sarebbe più affidabile dal momento che non è nemmeno esplicito. È perché le specifiche dicono che deve fare che sia sempre implementato in quel modo?
jgmjgm,

3
@jgmjgm è perché più affidabile with foo() as f: [...]è fondamentalmente la stessa f = foo(), f.__enter__()[...] e f.__exit__() con le eccezioni gestite , in modo che __exit__si chiama sempre. Quindi il file viene sempre chiuso.
Neingeist,

104

Puoi usare pathlib .

Per Python 3.5 e versioni successive:

from pathlib import Path
contents = Path(file_path).read_text()

Per le versioni precedenti di Python utilizzare pathlib2 :

$ pip install pathlib2

Poi:

from pathlib2 import Path
contents = Path(file_path).read_text()

Questa è l' read_text implementazione effettiva :

def read_text(self, encoding=None, errors=None):
    """
    Open the file in text mode, read it, and close the file.
    """
    with self.open(mode='r', encoding=encoding, errors=errors) as f:
        return f.read()

2

Bene, se devi leggere il file riga per riga per lavorare con ogni riga, puoi usare

with open('Path/to/file', 'r') as f:
    s = f.readline()
    while s:
        # do whatever you want to
        s = f.readline()

O ancora meglio:

with open('Path/to/file') as f:
    for line in f:
        # do whatever you want to

0

Invece di recuperare il contenuto del file come singola stringa, può essere utile archiviare il contenuto come un elenco di tutte le righe che comprende il file :

with open('Path/to/file', 'r') as content_file:
    content_list = content_file.read().strip().split("\n")

Come si può vedere, è necessario aggiungere i metodi concatenati .strip().split("\n")alla risposta principale in questo thread .

Qui, .strip()rimuove solo gli spazi bianchi e i caratteri di nuova riga alla fine dell'intera stringa di file e .split("\n")produce l'elenco effettivo dividendo l'intera stringa di file in corrispondenza di ogni carattere di nuova riga \ n .

Inoltre, in questo modo l'intero contenuto del file può essere archiviato in una variabile, il che potrebbe essere desiderato in alcuni casi, invece di passare in rassegna il file riga per riga, come sottolineato in questa risposta precedente .

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.