Pertanyaan Periksa apakah objek seperti file dengan Python


Objek mirip file adalah objek dalam Python yang berperilaku seperti file nyata, mis. memiliki metode baca () dan tulis (), tetapi memiliki implementasi yang berbeda. Ini dan realisasi dari Bebek Mengetik konsep.

Praktik ini dianggap baik untuk memungkinkan objek mirip file di mana-mana tempat file diharapkan sehingga mis. Sebuah StringIO atau objek Socket dapat digunakan sebagai pengganti file nyata. Jadi itu buruk untuk melakukan pemeriksaan seperti ini:

if not isinstance(fp, file):
   raise something

Apa cara terbaik untuk memeriksa apakah suatu objek (misalnya parameter dari suatu metode) adalah "file-like"?


75
2017-11-02 13:13


asal


Jawaban:


Umumnya tidak bagus untuk memeriksa seperti ini di kode Anda kecuali Anda memiliki persyaratan khusus.

Dalam Python pengetikan bersifat dinamis, mengapa Anda merasa perlu memeriksa apakah objek tersebut adalah file seperti, daripada hanya menggunakannya seolah-olah itu adalah file dan menangani kesalahan yang dihasilkan?

Setiap cek yang dapat Anda lakukan akan terjadi pada saat runtime pula jadi melakukan sesuatu seperti if not hasattr(fp, 'read') dan meningkatkan beberapa pengecualian memberikan sedikit lebih banyak manfaat daripada hanya menelepon fp.read() dan menangani kesalahan atribut yang dihasilkan jika metode tidak ada.


43
2017-11-02 13:29



Seperti yang orang lain katakan Anda harus secara umum menghindari pemeriksaan semacam itu. Satu pengecualian adalah ketika objek mungkin sah menjadi tipe yang berbeda dan Anda ingin perilaku yang berbeda tergantung pada jenisnya. Metode EAFP tidak selalu berfungsi di sini karena objek bisa terlihat seperti lebih dari satu jenis bebek!

Sebagai contoh, seorang pelaku 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 melempar pengecualian. Pada dasarnya konstruksi ini meniru fungsi overloading dan sangat tidak Pythonic, tetapi dapat berguna jika digunakan dengan hati-hati.

Sebagai catatan samping, Anda tidak dapat melakukan pemeriksaan file dengan cara yang sama dengan Python 3. Anda akan membutuhkan sesuatu seperti isinstance(f, io.IOBase) sebagai gantinya.


43
2017-11-02 13:52



Untuk 3,1+, salah satu dari yang berikut:

isinstance(something, io.TextIOBase)
isinstance(something, io.BufferedIOBase)
isinstance(something, io.RawIOBase)
isinstance(something, io.IOBase)

Untuk 2.x, "file-like object" adalah objek yang terlalu tidak jelas untuk diperiksa, tetapi dokumentasi untuk fungsi apa pun yang Anda hadapi akan memberi tahu Anda apa yang sebenarnya mereka butuhkan; jika tidak, baca kodenya.


Saat jawaban lain menunjukkan, hal pertama yang harus ditanyakan adalah apa yang sebenarnya Anda periksa. Biasanya, EAFP sudah cukup, dan lebih idiomatis.

Glosarium mengatakan "file-like object" adalah sinonim untuk "file object", yang pada akhirnya berarti itu adalah contoh salah satu dari ketiganya kelas dasar abstrak didefinisikan dalam itu io modul, yang semuanya merupakan subclass dari IOBase. Jadi, cara untuk memeriksa persis seperti yang ditunjukkan di atas.

(Namun, memeriksa IOBase sangat tidak berguna. Dapatkah Anda membayangkan sebuah kasus di mana Anda perlu membedakan file yang sebenarnya read(size) dari beberapa fungsi satu argumen bernama read itu tidak seperti file, tanpa juga perlu membedakan antara file teks dan file biner mentah? Jadi, sebenarnya, Anda hampir selalu ingin memeriksa, misalnya, "adalah objek file teks", bukan "adalah objek mirip file".)


Untuk 2.x, sedangkan io modul sudah ada sejak 2.6+, objek file built-in bukan merupakan contoh io kelas, juga tidak ada objek seperti file di stdlib, dan juga bukan objek file seperti pihak ketiga yang mungkin Anda temui. Tidak ada definisi resmi apa arti "file-like object"; hanya "sesuatu seperti builtin objek file", dan fungsi yang berbeda berarti hal yang berbeda dengan" seperti ". Fungsi seperti itu harus mendokumentasikan apa yang mereka maksud; jika tidak, Anda harus melihat kode.

Namun, makna yang paling umum adalah "memiliki read(size)"," telah read()", atau" merupakan string yang dapat dipecahkan ", tetapi beberapa pustaka lama mungkin berharap readline bukan salah satu dari mereka, beberapa perpustakaan suka close() file yang Anda berikan kepada mereka, beberapa akan mengharapkan itu jika fileno hadir maka fungsi lain tersedia, dll. Dan demikian pula untuk write(buf) (meskipun ada banyak pilihan yang lebih sedikit ke arah itu).


39
2017-07-17 19:36



Paradigma yang dominan di sini adalah EAFP: lebih mudah meminta maaf daripada izin. Pergi ke depan dan gunakan antarmuka file, kemudian menangani pengecualian yang dihasilkan, atau biarkan mereka merambat ke pemanggil.


27
2017-11-02 13:25



Ini sering berguna untuk meningkatkan kesalahan dengan memeriksa suatu kondisi, ketika kesalahan itu biasanya tidak akan dinaikkan sampai jauh di kemudian hari. 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 suatu kondisi berarti kesalahan mungkin terjadi yang bisa ditangkap 100 baris sebelumnya, atau di kelas super bukannya dibesarkan di subkelas, maka saya katakan tidak ada yang salah dengan pemeriksaan.

Memeriksa jenis yang tepat juga masuk akal ketika Anda menerima lebih dari satu jenis. Lebih baik untuk meningkatkan pengecualian yang mengatakan "Saya memerlukan subkelas dari basestring, OR file" daripada hanya meningkatkan pengecualian karena beberapa variabel tidak memiliki metode 'mencari' ...

Ini tidak berarti Anda menjadi gila dan melakukan hal ini di mana-mana, karena sebagian besar saya setuju dengan konsep pengecualian yang membesarkan diri mereka sendiri, tetapi jika Anda dapat membuat API Anda secara drastis jelas, atau menghindari eksekusi kode yang tidak perlu karena kondisi yang sederhana belum terpenuhi. lakukan itu!


10
2017-07-29 02:04



Anda dapat mencoba dan memanggil metode kemudian menangkap pengecualian:

try:
    fp.read()
except AttributeError:
    raise something

Jika Anda hanya ingin metode baca dan tulis, Anda dapat melakukan ini:

if not (hasattr(fp, 'read') and hasattr(fp, 'write')):
   raise something

Jika saya adalah Anda, saya akan pergi dengan metode try / except.


6
2017-11-02 13:15



Dalam sebagian besar keadaan, cara terbaik untuk menangani ini adalah tidak. Jika suatu metode mengambil objek seperti file, dan ternyata objek yang diteruskan tidak, pengecualian yang muncul ketika metode mencoba untuk menggunakan objek tidak kurang informatif daripada pengecualian yang mungkin telah Anda ajukan 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 telah Anda berikan, misalnya jika itu diatur dalam konstruktor kelas. Dalam hal ini, saya akan berpikir bahwa prinsip EAFP dibuat dengan prinsip "gagal cepat." Saya akan memeriksa objek untuk memastikannya menerapkan metode yang dibutuhkan kelas saya (dan itu metode mereka), misalnya:

class C():
    def __init__(self, file):
        if type(getattr(file, 'read')) != type(self.__init__):
            raise AttributeError
        self.file = file

2
2017-11-03 16:42



Saya akhirnya masuk ke pertanyaan Anda ketika saya sedang menulis open-seperti fungsi yang dapat menerima nama file, deskriptor file atau objek seperti file yang dibuka sebelumnya.

Daripada menguji untuk a read metode, seperti saran jawaban lainnya, saya akhirnya memeriksa apakah objek dapat dibuka. Jika bisa, itu string atau deskriptor, dan saya memiliki objek seperti file yang valid di tangan dari hasilnya. Jika open menimbulkan a TypeError, maka objek tersebut sudah menjadi file.


0
2018-04-28 17:13