Objek mirip file adalah objek dalam Python yang berperilaku seperti file nyata, misalnya memiliki metode baca () dan tulis (), tetapi memiliki implementasi yang berbeda. Ini adalah dan realisasi dari konsep Mengetik Bebek .
Ini dianggap sebagai praktik yang baik untuk mengizinkan objek seperti file di mana pun di mana file diharapkan sehingga misalnya objek StringIO atau Socket dapat digunakan sebagai pengganti file nyata. Jadi tidak baik melakukan pemeriksaan seperti ini:
if not isinstance(fp, file):
raise something
Apa cara terbaik untuk memeriksa apakah suatu objek (misalnya parameter metode) "seperti file"?
why
bagaimana dengan operator yang disukai__add__
,__lshift__
atau__or__
di kelas khusus? (objek file dan API: docs.python.org/glossary.html#term-file-object )Untuk 3.1+, salah satu dari berikut ini:
Untuk 2.x, "file-like object" merupakan hal yang terlalu kabur untuk diperiksa, tetapi dokumentasi untuk fungsi apa pun yang Anda hadapi diharapkan akan memberi tahu Anda apa yang sebenarnya mereka butuhkan; jika tidak, baca kodenya.
Seperti jawaban lain menunjukkan, hal pertama yang harus ditanyakan adalah apa yang sebenarnya Anda periksa. Biasanya, EAFP sudah cukup, dan lebih idiomatis.
Glossary yang mengatakan "file-seperti objek" adalah sinonim untuk "file objek", yang akhirnya berarti itu sebuah contoh dari salah satu dari tiga kelas dasar abstrak didefinisikan dalam satu
io
modul , yang dengan sendirinya semua subclass dariIOBase
. Jadi cara mengeceknya persis seperti gambar di atas.(Namun, pemeriksaan
IOBase
tidak terlalu berguna. Dapatkah Anda membayangkan kasus di mana Anda perlu membedakan file-like yang sebenarnyaread(size)
dari beberapa fungsi satu-argumen bernamaread
yang bukan file-like, tanpa juga perlu membedakan antara file teks dan raw file biner? Jadi, sungguh, Anda hampir selalu ingin memeriksa, misalnya, "adalah objek file teks", bukan "adalah objek seperti file".)Untuk 2.x, sementara
io
modul sudah ada sejak 2.6+, objek file bawaan bukanlah instanceio
kelas, begitu pula objek seperti file di stdlib, dan juga bukan sebagian besar objek seperti file pihak ketiga. Anda akan bertemu. Tidak ada definisi resmi tentang apa arti "objek seperti file"; itu hanya "sesuatu seperti objek file bawaan ", dan fungsi yang berbeda berarti hal yang berbeda dengan "suka". Fungsi tersebut harus mendokumentasikan apa yang mereka maksud; jika tidak, Anda harus melihat kodenya.Namun, arti yang paling umum adalah "has
read(size)
", "hasread()
", atau "is an iterable of strings", tetapi beberapa pustaka lama mungkin mengharapkanreadline
alih-alih salah satunya, beberapa pustaka menyukaiclose()
file yang Anda berikan padanya, beberapa akan mengharapkan bahwa jikafileno
hadir maka fungsionalitas lain tersedia, dll. Dan juga untukwrite(buf)
(meskipun ada lebih sedikit opsi ke arah itu).sumber
IOBase
. Misalnya perlengkapan pytest memberi Anda_pytest.capture.EncodedFile
yang tidak mewarisi dari apa pun.Seperti yang dikatakan orang lain, Anda sebaiknya menghindari pemeriksaan semacam itu. Satu pengecualian adalah ketika objek mungkin merupakan tipe yang berbeda dan Anda menginginkan perilaku yang berbeda tergantung pada tipenya. Metode EAFP tidak selalu berfungsi di sini karena sebuah objek dapat terlihat seperti lebih dari satu jenis bebek!
Misalnya seorang penginisialisasi dapat mengambil file, string atau instance dari kelasnya sendiri. Anda mungkin memiliki kode seperti:
class A(object): def __init__(self, f): if isinstance(f, A): # Just make a copy. elif isinstance(f, file): # initialise from the file else: # treat f as a string
Menggunakan EAFP di sini dapat menyebabkan segala macam masalah halus karena setiap jalur inisialisasi dijalankan sebagian sebelum mengeluarkan pengecualian. Pada dasarnya konstruksi ini meniru fungsi overloading sehingga tidak terlalu Pythonic, tetapi dapat berguna jika digunakan dengan hati-hati.
Sebagai catatan tambahan, Anda tidak dapat melakukan pemeriksaan file dengan cara yang sama di Python 3. Anda akan membutuhkan sesuatu seperti itu
isinstance(f, io.IOBase)
.sumber
Paradigma dominan di sini adalah EAFP: lebih mudah meminta maaf daripada izin. Lanjutkan dan gunakan antarmuka file, lalu tangani pengecualian yang dihasilkan, atau biarkan mereka menyebar ke pemanggil.
sumber
x
tidak seperti file, makax.read()
akan memunculkan pengecualiannya sendiri. Mengapa menulis pernyataan-if tambahan? Gunakan saja objeknya. Ini akan berhasil atau rusak.Seringkali berguna untuk meningkatkan kesalahan dengan memeriksa suatu kondisi, ketika kesalahan itu biasanya tidak akan dimunculkan sampai nanti. Hal ini terutama berlaku untuk batas antara kode 'user-land' dan 'api'.
Anda tidak akan menempatkan detektor logam di kantor polisi di pintu keluar, Anda akan menempatkannya di pintu masuk! Jika tidak memeriksa kondisi berarti mungkin terjadi kesalahan yang bisa saja tertangkap 100 baris sebelumnya, atau di kelas super alih-alih dimunculkan di subkelas maka saya katakan tidak ada yang salah dengan pemeriksaan.
Memeriksa jenis yang tepat juga masuk akal bila Anda menerima lebih dari satu jenis. Lebih baik memunculkan pengecualian yang mengatakan "Saya memerlukan subkelas basestring, OR file" daripada hanya meningkatkan pengecualian karena beberapa variabel tidak memiliki metode 'seek' ...
Ini tidak berarti Anda menjadi gila dan melakukan ini di mana-mana, sebagian besar saya setuju dengan konsep pengecualian yang meningkat dengan sendirinya, tetapi jika Anda dapat membuat API Anda sangat jelas, atau menghindari eksekusi kode yang tidak perlu karena kondisi sederhana belum terpenuhi lakukanlah!
sumber
Anda dapat mencoba dan memanggil metode tersebut lalu menangkap pengecualian:
try: fp.read() except AttributeError: raise something
Jika Anda hanya menginginkan metode baca dan tulis, Anda dapat melakukan ini:
if not (hasattr(fp, 'read') and hasattr(fp, 'write')): raise something
Jika saya jadi Anda, saya akan menggunakan metode coba / kecuali.
sumber
try
selalu menjadi pilihan pertama. Thehasattr
cek hanya - untuk beberapa alasan yang sangat jelas - Anda tidak dapat hanya menggunakantry
.fp.read(0)
bukannyafp.read()
untuk menghindari meletakkan semua kode ditry
blok jika Anda ingin memproses data darifp
selanjutnya.fp.read()
dengan file besar akan segera meningkatkan penggunaan memori.Flask
saya melakukan ini dan menyadari bahwaFileStorage
objek yang mendasari memerlukan reset pointer setelah dibaca.Dalam banyak situasi, cara terbaik untuk menangani hal ini adalah dengan tidak melakukannya. Jika suatu metode mengambil objek seperti file, dan ternyata objek yang diteruskannya bukan, pengecualian yang dimunculkan saat metode mencoba menggunakan objek tersebut tidak kurang informatifnya daripada pengecualian apa pun yang mungkin Anda kemukakan secara eksplisit.
Setidaknya ada satu kasus di mana Anda mungkin ingin melakukan pemeriksaan semacam ini, dan saat itulah objek tidak segera digunakan oleh apa yang Anda berikan padanya, misalnya jika itu disetel di konstruktor kelas. Dalam hal ini, saya akan berpikir bahwa prinsip EAFP dikalahkan oleh prinsip "gagal cepat." Saya akan memeriksa objek untuk memastikan itu menerapkan metode yang dibutuhkan kelas saya (dan itu adalah metode), misalnya:
class C(): def __init__(self, file): if type(getattr(file, 'read')) != type(self.__init__): raise AttributeError self.file = file
sumber
getattr(file, 'read')
bukan hanyafile.read
? Ini melakukan hal yang persis sama.file
contoh aktual . (Metode contoh tipe builtin / C-extension adalah tipebuiltin_function_or_method
, sedangkan class gaya lama adalahinstancemethod
). Fakta bahwa ini adalah kelas gaya lama, dan yang digunakan==
pada tipe, bukanininstance
atauissubclass
, adalah masalah lebih lanjut, tetapi jika ide dasarnya tidak berhasil, itu hampir tidak menjadi masalah.Saya akhirnya menemukan pertanyaan Anda ketika saya menulis
open
fungsi -like yang dapat menerima nama file, deskriptor file atau objek seperti file yang sudah dibuka sebelumnya.Alih-alih menguji suatu
read
metode, seperti yang disarankan oleh jawaban lain, saya akhirnya memeriksa apakah benda itu dapat dibuka. Jika bisa, itu adalah string atau deskriptor, dan saya memiliki objek seperti file yang valid dari hasil. Jikaopen
memunculkan aTypeError
, maka objek tersebut sudah menjadi file.sumber