Di zaman prasejarah (Python 1.4) kami melakukan:
fp = open('filename.txt')
while 1:
line = fp.readline()
if not line:
break
print line
setelah Python 2.1, kami melakukan:
for line in open('filename.txt').xreadlines():
print line
sebelum kami mendapatkan protokol iterator yang nyaman di Python 2.3, dan dapat melakukan:
for line in open('filename.txt'):
print line
Saya telah melihat beberapa contoh menggunakan lebih banyak verbose:
with open('filename.txt') as fp:
for line in fp:
print line
apakah ini metode yang disukai untuk maju?
[Sunting] Saya mengerti bahwa pernyataan with memastikan penutupan file ... tetapi mengapa itu tidak termasuk dalam protokol iterator untuk objek file?
python
python-3.x
python-2.7
thebjorn
sumber
sumber
Jawaban:
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
with
blok 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
with
blok saja.Pertanyaan Bonus
(Hentikan membaca sekarang jika hanya tertarik pada aspek obyektif dari pertanyaan tersebut.)
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
with
blok 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.
sumber
with
memang memberi Anda ketenangan pikiran, jadi ini masih merupakan praktik terbaik.Iya,
with open('filename.txt') as fp: for line in fp: print line
adalah cara untuk pergi.
Ini tidak lebih bertele-tele. Itu lebih aman.
sumber
jika Anda dimatikan oleh baris tambahan, Anda dapat menggunakan fungsi pembungkus seperti ini:
def with_iter(iterable): with iterable as iter: for item in iter: yield item for line in with_iter(open('...')): ...
di Python 3.3,
yield from
pernyataan itu akan membuat ini lebih pendek:def with_iter(iterable): with iterable as iter: yield from iter
sumber
f = open('test.txt','r') for line in f.xreadlines(): print line f.close()
sumber