Misalkan saya memiliki fungsi yang melakukan hal-hal dengan file teks - misalnya membaca dari itu dan menghapus kata 'a'. Saya bisa memberikannya nama file dan menangani pembukaan / penutupan fungsi, atau saya bisa memberikannya file yang dibuka dan berharap siapa pun yang memanggilnya akan berurusan dengan menutupnya.
Cara pertama sepertinya cara yang lebih baik untuk menjamin tidak ada file yang dibiarkan terbuka, tetapi mencegah saya menggunakan hal-hal seperti objek StringIO
Cara kedua bisa sedikit berbahaya - tidak ada cara untuk mengetahui apakah file akan ditutup atau tidak, tapi saya akan dapat menggunakan objek seperti file
def ver_1(filename):
with open(filename, 'r') as f:
return do_stuff(f)
def ver_2(open_file):
return do_stuff(open_file)
print ver_1('my_file.txt')
with open('my_file.txt', 'r') as f:
print ver_2(f)
Apakah salah satu dari ini umumnya disukai? Apakah umumnya diharapkan bahwa suatu fungsi akan berperilaku dalam salah satu dari dua cara ini? Atau haruskah itu didokumentasikan dengan baik sehingga programmer dapat menggunakan fungsi yang sesuai?
sumber
your_function
dapat digunakan dalam hal ini.Pertanyaan sesungguhnya adalah kelengkapan. Apakah fungsi pemrosesan file Anda adalah pemrosesan file yang lengkap, atau hanya sepotong dalam rangkaian langkah pemrosesan? Jika sudah lengkap dan dengan sendirinya, maka merasa bebas untuk merangkum semua akses file dalam suatu fungsi.
Ini memiliki properti yang sangat bagus untuk menyelesaikan sumber daya (menutup file) di akhir
with
pernyataan.Namun jika ada kemungkinan untuk memproses file yang sudah terbuka, maka perbedaan Anda
ver_1
danver_2
lebih masuk akal. Sebagai contoh:Semacam ini pengujian tipe eksplisit sering disukai , terutama dalam bahasa seperti Java, Julia, dan Go mana jenis-atau pengiriman berbasis antarmuka langsung didukung. Namun, dalam Python, tidak ada dukungan bahasa untuk pengiriman berbasis tipe. Terkadang Anda mungkin melihat kritik pengujian tipe langsung dalam Python, tetapi dalam praktiknya itu sangat umum dan cukup efektif. Ini memungkinkan suatu fungsi memiliki tingkat umum yang tinggi, menangani tipe data apa pun yang mungkin muncul, alias "mengetik bebek." Perhatikan garis bawah utama aktif
_ver_file
; itu adalah cara konvensional untuk menetapkan fungsi (atau metode) "pribadi". Meskipun secara teknis dapat dipanggil langsung, itu menunjukkan bahwa fungsi tidak dimaksudkan untuk konsumsi eksternal langsung.Pembaruan 2019: Diberikan pembaruan terbaru dalam Python 3, misalnya jalur sekarang berpotensi disimpan sebagai
pathlib.Path
objek bukan hanyastr
ataubytes
(3,4+), dan tipe yang mengisyaratkan telah berubah dari esoterik menjadi arus utama (sekitar 3,6+, meskipun masih aktif berkembang), inilah kode yang diperbarui yang memperhitungkan kemajuan ini:sumber
read
sesuatu yang mungkin seperti file, atau memanggilopen(fileobj, 'r')
dan menangkapTypeError
iffileobj
bukan string.ver
operasi independen dari jenis. Mungkin juga bisa diterapkanver
melalui mengetik bebek, seperti yang Anda katakan. Tapi menghasilkan pengecualian kemudian lebih lambat dari inspeksi tipe sederhana, dan IMO tidak menghasilkan manfaat tertentu (kejelasan, generalisasi, dll.) Dalam pengalaman saya, mengetik bebek adalah luar biasa "dalam jumlah besar," tetapi netral untuk kontraproduktif "dalam skala kecil . "hasattr(fileobj, 'read')
tes akan mengetik bebek; sebuahisinstance(fileobj, str)
tes tidak. Inilah contoh perbedaannya:isinstance
tes gagal dengan nama file unicode, karenau'adsf.txt'
bukan astr
. Anda telah menguji jenis yang terlalu spesifik. Tes mengetik bebek, apakah berdasarkan panggilanopen
ataudoes_this_object_represent_a_filename
fungsi hipotetis , tidak akan memiliki masalah itu.is_instance(x, str)
tetapi lebih sepertiis_instance(x, string_types)
, denganstring_types
diatur dengan benar untuk operasi yang tepat di PY2 dan PY3. Mengingat sesuatu yang dukun seperti tali,ver
akan bereaksi dengan benar; diberi sesuatu yang dukun seperti file, sama. Untuk sebuah pengguna dariver
, tidak akan ada perbedaan - kecuali bahwa pelaksanaan jenis pemeriksaan akan berjalan lebih cepat. Bebek puritan: jangan ragu untuk tidak setuju.Jika Anda memberikan nama file di sekitar alih-alih menangani file maka tidak ada jaminan bahwa file kedua adalah file yang sama dengan yang pertama saat dibuka; ini dapat menyebabkan bug dan lubang keamanan benar.
sumber
Ini tentang kepemilikan dan tanggung jawab untuk menutup file. Anda dapat meneruskan aliran atau file menangani atau hal apa pun yang harus ditutup / dibuang di beberapa titik ke metode lain, selama Anda memastikan jelas siapa yang memilikinya dan yakin itu akan ditutup oleh pemilik ketika Anda selesai . Ini biasanya melibatkan konstruksi try-akhirnya atau pola pakai.
sumber
Jika Anda memilih untuk meneruskan file yang terbuka, Anda dapat melakukan sesuatu seperti TETAPI berikut ini, Anda tidak memiliki akses ke nama file dalam fungsi yang menulis ke dalam file.
Saya akan melakukan ini jika saya ingin memiliki kelas yang 100% bertanggung jawab atas operasi file / stream dan kelas atau fungsi lain yang akan naif dan tidak diharapkan untuk membuka atau menutup file / stream tersebut.
Ingat bahwa manajer konteks bekerja seperti memiliki klausa akhirnya. Jadi, jika pengecualian dilemparkan dalam fungsi penulis, file akan ditutup tidak peduli apa.
sumber
with open
? Bagaimana ini menjawab pertanyaan tentang menggunakan nama file vs objek seperti file?with open
, kan? Dan yang Anda advokasi secara efektif adalah fungsi yang hanya menggunakan objek seperti file, dan tidak peduli dari mana asalnya?