Tepat ada satu alasan mengapa hal berikut lebih disukai:
with open('filename.txt') as fp:
for line in fp:
print line
Kita semua dimanjakan oleh skema penghitungan referensi yang relatif deterministik dari CPython untuk pengumpulan sampah. Implementasi hipotetis lain dari Python tidak akan selalu menutup file "cukup cepat" tanpa withblok jika mereka menggunakan skema lain untuk merebut kembali memori.
Dalam implementasi seperti itu, Anda mungkin mendapatkan kesalahan "terlalu banyak file yang dibuka" dari OS jika kode Anda membuka file lebih cepat daripada pemanggil sampah yang memanggil finalizer pada pegangan file yatim piatu. Solusi yang biasa dilakukan adalah dengan segera memicu GC, tetapi ini adalah peretasan yang buruk dan harus dilakukan oleh setiap fungsi yang dapat mengalami kesalahan, termasuk yang ada di perpustakaan. Sungguh mimpi buruk.
Atau Anda bisa menggunakan withblok saja.
Pertanyaan Bonus
(Hentikan membaca sekarang jika hanya tertarik pada aspek obyektif dari pertanyaan tersebut.)
Mengapa itu tidak termasuk dalam protokol iterator untuk objek file?
Ini adalah pertanyaan subjektif tentang desain API, jadi saya memiliki jawaban subjektif dalam dua bagian.
Pada dasarnya, hal ini terasa salah, karena membuat protokol iterator melakukan dua hal terpisah — mengulang baris dan menutup pegangan file — dan sering kali merupakan ide yang buruk untuk membuat fungsi yang tampak sederhana melakukan dua tindakan. Dalam hal ini, rasanya sangat buruk karena iterator berhubungan dengan cara kuasi-fungsional, berbasis nilai dengan konten file, tetapi mengelola pegangan file adalah tugas yang sepenuhnya terpisah. Menggabungkan keduanya, tanpa terlihat, menjadi satu tindakan, mengejutkan manusia yang membaca kode dan membuatnya lebih sulit untuk bernalar tentang perilaku program.
Bahasa lain pada dasarnya sampai pada kesimpulan yang sama. Haskell secara singkat tergoda dengan apa yang disebut "lazy IO" yang memungkinkan Anda untuk mengulang file dan menutupnya secara otomatis ketika Anda sampai ke akhir streaming, tetapi hampir secara universal tidak disarankan untuk menggunakan lazy IO di Haskell hari ini, dan Haskell sebagian besar pengguna telah pindah ke manajemen sumber daya yang lebih eksplisit seperti Conduit yang berperilaku lebih seperti withblok di Python.
Pada tingkat teknis, ada beberapa hal yang mungkin ingin Anda lakukan dengan pegangan file dengan Python yang tidak akan berfungsi dengan baik jika iterasi menutup pegangan file. Misalnya, saya perlu mengulang file dua kali:
with open('filename.txt') as fp:
for line in fp:
...
fp.seek(0)
for line in fp:
...
Meskipun ini adalah kasus penggunaan yang kurang umum, pertimbangkan fakta bahwa saya mungkin baru saja menambahkan tiga baris kode di bagian bawah ke basis kode yang ada yang awalnya memiliki tiga baris teratas. Jika iterasi menutup file, saya tidak akan bisa melakukan itu. Jadi, menjaga iterasi dan manajemen sumber daya tetap terpisah membuatnya lebih mudah untuk menyusun potongan kode menjadi program Python yang lebih besar dan berfungsi.
Komposabilitas adalah salah satu fitur kegunaan terpenting dari suatu bahasa atau API.