Apakah membaca seluruh file membiarkan file terbuka?

372

Jika Anda membaca seluruh file dengan content = open('Path/to/file', 'r').read()apakah pegangan file dibiarkan terbuka sampai skrip keluar? Apakah ada metode yang lebih ringkas untuk membaca seluruh file?

tMC
sumber

Jawaban:

585

Jawaban atas pertanyaan itu agak tergantung pada implementasi Python tertentu.

Untuk memahami semua ini, berikan perhatian khusus pada fileobjek yang sebenarnya . Dalam kode Anda, objek itu disebutkan hanya sekali, dalam ekspresi, dan menjadi tidak dapat diakses segera setelah read()panggilan kembali.

Ini berarti bahwa objek file adalah sampah. Satu-satunya pertanyaan yang tersisa adalah "Kapan pemungut sampah mengumpulkan objek file?".

dalam CPython, yang menggunakan penghitung referensi, jenis sampah ini langsung diperhatikan, dan karenanya akan segera dikumpulkan. Ini biasanya tidak berlaku untuk implementasi python lainnya.

Solusi yang lebih baik, untuk memastikan bahwa file ditutup, adalah pola ini:

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

yang akan selalu menutup file segera setelah blok berakhir; bahkan jika pengecualian terjadi.

Sunting: Untuk memberikan poin yang lebih baik:

Selain file.__exit__(), yang "secara otomatis" dipanggil dalam withpengaturan manajer konteks, satu-satunya cara lain yang file.close()secara otomatis dipanggil (yaitu, selain secara eksplisit menyebutnya sendiri,) adalah melalui file.__del__(). Ini membawa kita pada pertanyaan kapan __del__()dipanggil?

Program yang ditulis dengan benar tidak dapat mengasumsikan bahwa finalis akan pernah berjalan pada titik mana pun sebelum penghentian program.

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

Khususnya:

Objek tidak pernah secara eksplisit dihancurkan; Namun, ketika mereka menjadi tidak terjangkau mereka mungkin menjadi sampah. Suatu implementasi diperbolehkan untuk menunda pengumpulan sampah atau menghilangkannya sama sekali - ini adalah masalah kualitas implementasi bagaimana pengumpulan sampah dilaksanakan, selama tidak ada benda yang dikumpulkan yang masih dapat dijangkau.

[...]

CPython saat ini menggunakan skema penghitungan referensi dengan deteksi (opsional) yang tertunda terhadap sampah yang terhubung secara siklikal, yang mengumpulkan sebagian besar objek segera setelah mereka menjadi tidak terjangkau, tetapi tidak dijamin untuk mengumpulkan sampah yang mengandung referensi melingkar.

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

(Penekanan milikku)

tetapi seperti yang disarankan, implementasi lain mungkin memiliki perilaku lain. Sebagai contoh, PyPy memiliki 6 implementasi pengumpulan sampah yang berbeda !

SingleNegationElimination
sumber
24
Untuk sementara, sebenarnya tidak ada implementasi Python lainnya; tetapi mengandalkan detail implementasi tidak benar-benar Pythonic.
Karl Knechtel
Apakah masih spesifik implementasi, atau sudah distandarisasi? Tidak memanggil __exit__()dalam kasus seperti itu terdengar seperti cacat desain.
rr-
2
@jgmjgm Justru karena 3 masalah tersebut, GC tidak dapat diprediksi, try/ finallymenjadi fiddly dan sangat berguna bagi petugas kebersihan yang withmemecahkan masalah. Perbedaan antara "menutup secara eksplisit" dan "mengelola dengan with" adalah bahwa penangan keluar dipanggil meskipun ada pengecualian. Anda bisa memasukkannya ke close()dalam finallyklausa, tetapi itu tidak jauh berbeda dengan menggunakan with, sedikit lebih berantakan (3 baris tambahan, bukan 1), dan sedikit lebih sulit untuk membuatnya dengan benar.
SingleNegationElimination
1
Apa yang saya tidak dapatkan adalah mengapa 'dengan' akan lebih dapat diandalkan karena tidak eksplisit juga. Apakah karena spec mengatakan harus melakukan itu selalu dilaksanakan seperti itu?
jgmjgm
3
@jgmjgm itu karena lebih dapat diandalkan with foo() as f: [...]pada dasarnya adalah sama f = foo(), f.__enter__(), [...] dan f.__exit__() dengan pengecualian ditangani , sehingga __exit__selalu disebut. Jadi file selalu ditutup.
neingeist
104

Anda dapat menggunakan pathlib .

Untuk Python 3.5 dan lebih tinggi:

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

Untuk versi Python yang lebih lama, gunakan pathlib2 :

$ pip install pathlib2

Kemudian:

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

Ini adalah read_text implementasi aktual :

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()
Eyal Levin
sumber
2

Nah, jika Anda harus membaca file baris demi baris agar bisa bekerja dengan setiap baris, Anda bisa menggunakannya

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

Atau bahkan cara yang lebih baik:

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

Alih-alih mengambil konten file sebagai string tunggal, mungkin berguna untuk menyimpan konten sebagai daftar semua baris yang terdiri dari file :

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

Seperti yang bisa dilihat, kita perlu menambahkan metode gabungan .strip().split("\n")ke jawaban utama di utas ini .

Di sini, .strip()cukup hapus spasi putih dan karakter baris baru di akhir seluruh string file, dan .split("\n")buat daftar aktual melalui pemisahan seluruh string file di setiap karakter baris baru \ n .

Selain itu, dengan cara ini seluruh konten file dapat disimpan dalam variabel, yang mungkin diinginkan dalam beberapa kasus, alih-alih mengulangi file baris demi baris seperti yang ditunjukkan dalam jawaban sebelumnya .

Andreas L.
sumber