Apa cara kanonik untuk memeriksa jenis Python?

1278

Apa cara terbaik untuk memeriksa apakah objek yang diberikan adalah tipe yang diberikan? Bagaimana dengan mengecek apakah objek diwarisi dari jenis yang diberikan?

Katakanlah saya punya objek o. Bagaimana saya memeriksa apakah itu str?

Herge
sumber
7
Nah, pendekatan kanonik dengan Python adalah untuk tidak memeriksa jenisnya sama sekali (kecuali jika Anda sedang debug). Biasanya Anda hanya mencoba menggunakannya sebagai string (mis. Digabungkan dengan string lain, cetak ke konsol, dll.); jika Anda berpikir itu mungkin gagal, gunakan try / exception atau hasattr. Yang mengatakan, jawaban yang diterima adalah cara kanonik untuk melakukan apa yang umumnya Anda "tidak boleh lakukan" di dunia Python. Untuk info lebih lanjut, google "Python duck typing" atau baca ini: voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883/…
Jon Coombs
9
Saya pikir Mr. Coombs mengabaikan contoh-contoh seperti kelas serializable non-JSON. Jika meletakkan sepotong besar data melalui suatu fungsi (yang kodenya tidak dapat memengaruhi seseorang) orang mungkin ingin mengonversi bagian tertentu dari data itu menjadi, misalnya, a <str> sebelum meneruskannya. Setidaknya begitulah akhirnya saya sampai di halaman ini ...
John Carrell
2
Tampaknya alasan paling umum untuk meminta ini adalah bahwa seseorang ingin membedakan antara string dan iterables string. Ini adalah pertanyaan yang sulit karena string adalah iterables dari string - string karakter tunggal bahkan merupakan urutan dari dirinya sendiri (terakhir kali saya memeriksa - orang mungkin tidak harus bergantung padanya). Tapi apakah ada yang pernah menggunakan sesuatu seperti string? Ya . Jadi jawaban untuk "Apa yang harus saya lakukan untuk membedakan antara string dan iterables string lainnya?" benar: "Itu tergantung pada apa yang Anda coba lakukan". :-D
clacke
2
Anotasi jenis python sekarang menjadi sesuatu. Lihatlah mypy
Sheena

Jawaban:

1522

Untuk memeriksa apakah oturunan dari stratau subkelas apa pun str, gunakan isinstance (ini akan menjadi cara "kanonik"):

if isinstance(o, str):

Untuk memeriksa apakah jenisnya otepat str(tidak termasuk subkelas):

if type(o) is str:

Berikut ini juga berfungsi, dan dapat berguna dalam beberapa kasus:

if issubclass(type(o), str):

Lihat Fungsi Bawaan dalam Referensi Pustaka Python untuk informasi yang relevan.

Satu lagi catatan: dalam hal ini, jika Anda menggunakan Python 2, Anda mungkin sebenarnya ingin menggunakan:

if isinstance(o, basestring):

karena ini juga akan menangkap string Unicode ( unicodebukan subkelas str; keduanya strdan unicodemerupakan subklas dari basestring). Perhatikan bahwa basestringtidak ada lagi di Python 3, di mana ada pemisahan ketat string ( str) dan data biner ( bytes).

Atau, isinstanceterima tupel kelas. Ini akan kembali Truejika omerupakan turunan dari subkelas dari (str, unicode):

if isinstance(o, (str, unicode)):
Fredrik Johansson
sumber
31
str .__ subclass __ () hanya mengembalikan subclass langsung dari str, dan tidak melakukan hal yang sama seperti issubclass () atau isinstance (). (Untuk melakukan itu, Anda harus secara rekursif memanggil .__ subclass __ ().
Thomas Wouters
15
Ini adalah jawaban yang baik, tetapi saya pikir ini harus dimulai dengan peringatan bahwa Anda biasanya tidak boleh melakukan ini dengan Python. Seperti itu, tampaknya untuk memvalidasi asumsi bahwa ini adalah "hal kanonik untuk dilakukan dengan Python", yang tidak.
Jon Coombs
4
Ini adalah jawaban python2. Misalnya, tidak ada dasar pada python3.
dfrankow
4
Apa perbedaan antara instance dan "tepatnya"? Kalau type(a) is Objectbegitu bukankah itu benar juga isinstance(a, Object). Namun, jika type(a) is SubClassOfObject, maka type(a) is Object == False, tetapi isinstance(a, Object) == True. Baik?
mavavilj
1
@mavavilj - a is bberarti a dan b adalah hal yang persis sama, yaitu referensi ke entitas yang sama dalam memori. Jadi adan bharus kelas yang sama persis, bukan subclass, seperti dengan isinstance(). Lihat misalnya stackoverflow.com/a/133024/1072212
Terry Brown
196

Cara paling Pythonic untuk memeriksa jenis objek adalah ... bukan untuk memeriksanya.

Karena Python mendorong Bebek Mengetik , Anda harus try...exceptmenggunakan metode objek seperti yang Anda inginkan. Jadi, jika fungsi Anda mencari objek file yang dapat ditulis, jangan periksa apakah itu subkelas file, coba gunakan .write()metodenya!

Tentu saja, kadang-kadang abstraksi yang bagus ini rusak dan isinstance(obj, cls)itulah yang Anda butuhkan. Tapi gunakan hemat.

Dan Lenski
sumber
75
IMHO, cara paling Pythonic adalah untuk mengatasi argumen apa pun yang diberikan. Dalam kode saya, saya sering tidak dapat mengetahui apakah saya menerima objek atau array objek, dan saya menggunakan pemeriksaan tipe secara internal untuk mengonversi objek tunggal ke daftar satu elemen.
sastanin
14
Alih-alih hanya mencoba menggunakan metode tulisnya ada kalanya Anda ingin melakukan ini tanpa menyebabkan pengecualian. Dalam hal ini Anda dapat melakukan ... if hasattr(ob, "write") and callable(ob.write): Atau menyimpan akses dict ...func = getattr(ob, "write", None) if callable(func): ...
ideasman42
142
Mengetik bebek adalah tentang menggunakan perpustakaan. Pengecekan tipe adalah tentang menulis perpustakaan. Bukan domain masalah yang sama.
RickyA
16
@ RickyA, saya tidak setuju. Mengetik bebek adalah tentang berinteraksi dengan objek menggunakan antarmuka dengan semantik terkenal. Ini bisa berlaku baik untuk kode perpustakaan atau kode yang menggunakan perpustakaan seperti itu.
Dan Lenski
6
@ nyuszika7h, Dalam Python3 hasattrhanya menekan sebuah AttributeError - Lihat: docs.python.org/3.4/library/functions.html#hasattr
ideasman42
57

isinstance(o, str)akan kembali Truejika omerupakan stratau dari jenis yang mewarisi dari str.

type(o) is strakan kembali Truejika dan hanya jika ostr. Ini akan kembali Falsejika odari tipe yang mewarisi dari str.

Herge
sumber
6
Tentu saja, ini akan gagal jika objek tersebut bukan turunan dari 'str', tetapi dari sesuatu yang seperti string. Seperti unicode, mmap, UserString atau tipe lain yang ditentukan pengguna. Pendekatan biasa dengan Python adalah tidak melakukan typechecks.
Thomas Wouters
6
Anda tidak perlu meminta maaf, tidak apa-apa untuk menjawab pertanyaan Anda sendiri. SO adalah untuk jawabannya, bukan karma.
Eli Bendersky
2
Ini sangat membantu. Karena perbedaan antara isinstancedan type(var) == type('')tidak jelas.
sastanin
30

Setelah pertanyaan diajukan dan dijawab, ketik petunjuk ditambahkan ke Python . Ketik petunjuk dalam Python memungkinkan jenis diperiksa tetapi dengan cara yang sangat berbeda dari bahasa yang diketik secara statis. Ketik petunjuk dalam Python mengaitkan tipe argumen yang diharapkan dengan fungsi sebagai data yang dapat diakses runtime yang terkait dengan fungsi dan ini memungkinkan tipe diperiksa. Contoh sintaks petunjuk jenis:

def foo(i: int):
    return i

foo(5)
foo('oops')

Dalam hal ini kami ingin memicu kesalahan foo('oops')karena jenis argumen yang dijelaskan int. Petunjuk tipe yang ditambahkan tidak menyebabkan kesalahan terjadi ketika skrip dijalankan secara normal. Namun, itu menambahkan atribut ke fungsi yang menggambarkan tipe yang diharapkan yang dapat ditanyakan oleh program lain dan digunakan untuk memeriksa kesalahan tipe.

Salah satu dari program lain yang dapat digunakan untuk menemukan kesalahan jenis adalah mypy:

mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"

(Anda mungkin perlu menginstal mypydari manajer paket Anda. Saya tidak berpikir itu datang dengan CPython tetapi tampaknya memiliki beberapa tingkat "pejabat".)

Pengecekan tipe dengan cara ini berbeda dengan pengecekan tipe dalam bahasa yang dikompilasi secara statis. Karena tipe bersifat dinamis dalam Python, pengecekan tipe harus dilakukan saat runtime, yang membebankan biaya - bahkan pada program yang benar - jika kita bersikeras bahwa itu terjadi pada setiap kesempatan. Pemeriksaan tipe eksplisit juga mungkin lebih ketat dari yang dibutuhkan dan menyebabkan kesalahan yang tidak perlu (mis. Apakah argumennya benar-benar harus listbertipe tepat atau adakah yang cukup memadai?).

Kelebihan dari pemeriksaan tipe eksplisit adalah bahwa ia dapat menangkap kesalahan lebih awal dan memberikan pesan kesalahan yang lebih jelas daripada mengetik bebek. Persyaratan yang tepat dari jenis bebek hanya dapat dinyatakan dengan dokumentasi eksternal (mudah-mudahan itu menyeluruh dan akurat) dan kesalahan dari jenis yang tidak kompatibel dapat terjadi jauh dari tempat asalnya.

Petunjuk tipe Python dimaksudkan untuk menawarkan kompromi di mana tipe dapat ditentukan dan diperiksa tetapi tidak ada biaya tambahan selama eksekusi kode biasa.

The typingvariabel paket penawaran jenis yang dapat digunakan dalam jenis petunjuk untuk mengekspresikan perilaku yang dibutuhkan tanpa memerlukan jenis tertentu. Sebagai contoh, itu termasuk variabel seperti Iterabledan Callableuntuk petunjuk untuk menentukan kebutuhan untuk jenis apa pun dengan perilaku tersebut.

Meskipun petunjuk jenis adalah cara yang paling Pythonic untuk memeriksa jenis, seringkali bahkan lebih Pythonic untuk tidak memeriksa jenis sama sekali dan bergantung pada mengetik bebek. Petunjuk jenis relatif baru dan juri masih keluar ketika mereka solusi paling Pythonic. Perbandingan yang relatif tidak kontroversial tetapi sangat umum: Petunjuk jenis memberikan bentuk dokumentasi yang dapat ditegakkan, memungkinkan kode untuk menghasilkan lebih awal dan lebih mudah untuk memahami kesalahan, dapat menangkap kesalahan yang tidak dapat diketik oleh bebek, dan dapat diperiksa secara statis (dalam kondisi yang tidak biasa akal tapi masih di luar runtime). Di sisi lain, mengetik bebek telah menjadi cara Pythonic untuk waktu yang lama, tidak memaksakan overhead kognitif dari mengetik statis, kurang verbose, dan akan menerima semua jenis yang layak dan kemudian beberapa.

Praxeolitic
sumber
2
-1: mypy secara khusus menyebut dirinya "pemeriksa tipe statis" jadi saya tidak yakin dari mana Anda mendapat "pemeriksaan jenis harus dilakukan saat runtime" dari.
Kevin
@Kevin Dalam retrospeksi, itu adalah penyimpangan yang tidak perlu, tetapi untuk lebih dalam, petunjuk tipe Python diubah menjadi data runtime dan mypymerupakan modul Python yang digunakan importlibuntuk mengakses data itu. Apakah ini "pemeriksaan tipe statis" adalah pertanyaan filosofis tetapi berbeda dari apa yang kebanyakan diharapkan karena penerjemah bahasa normal dan mesin impor terlibat.
Praxeolitic
4
Itu juga tidak benar. Ini menggunakan typed_ast, yang itu sendiri hanya tiruan dari ast dengan fitur tambahan. ast tidak mengimpor modul; itu mem-parsing mereka menjadi pohon sintaksis abstrak.
Kevin
18

Berikut adalah contoh mengapa mengetik bebek itu jahat tanpa mengetahui kapan itu berbahaya. Misalnya: Berikut adalah kode Python (mungkin menghilangkan indentasi yang tepat), perhatikan bahwa situasi ini dapat dihindari dengan menjaga fungsi isinstance dan issubclassof untuk memastikan bahwa ketika Anda benar-benar membutuhkan bebek, Anda tidak mendapatkan bom.

class Bomb:
    def __init__(self):
        ""

    def talk(self):
        self.explode()

    def explode(self):
        print "BOOM!, The bomb explodes."

class Duck:
    def __init__(self):
        ""
    def talk(self):
        print "I am a duck, I will not blow up if you ask me to talk."    

class Kid:
    kids_duck = None

    def __init__(self):
        print "Kid comes around a corner and asks you for money so he could buy a duck."

    def takeDuck(self, duck):
        self.kids_duck = duck
        print "The kid accepts the duck, and happily skips along"

    def doYourThing(self):
        print "The kid tries to get the duck to talk"
        self.kids_duck.talk()

myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()
Dmitry
sumber
36
Bahkan dengan pengecekan tipe, Anda dapat membuat class EvilDuck(Duck)dan mengganti pembicaraan (). Atau lebih mungkin,, class ChineseCancerDuck(Duck)dengan efek samping buruk yang tidak muncul sampai bertahun-tahun kemudian. Anda akan lebih baik hanya mengawasi anak Anda (dan benar-benar menguji mainannya :)
Brett Thomas
36
Bom tidak bicara. Jangan menambahkan metode yang tidak masuk akal dan ini tidak akan terjadi.
sayap kanan
7
@Dmitry, ini adalah kritik umum dari Duck Typing: en.wikipedia.org/wiki/Duck_typing#Criticism ... Anda pada dasarnya mengatakan bahwa setiap antarmuka yang semantiknya tidak ditegakkan oleh bahasa adalah jahat. Saya percaya ini lebih merupakan pendekatan Jawa. Inti dari pengetikan bebek Python adalah bahwa pengerjaan itu hanya berfungsi ketika ada konvensi umum tentang apa arti antarmuka spesifik. Sebagai contoh, Anda bisa membuat banyak kode Python dengan mengesampingkan __file__atribut (biasanya digunakan untuk mengidentifikasi objek seperti file) berarti sesuatu yang lain.
Dan Lenski
2
Ini semua bermuara pada lelucon lama, "Dokter, sakit ketika saya melakukan ini." ... "Kalau begitu jangan lakukan itu." Tidak memuaskan bagi seseorang yang terbiasa "jika ia mengkompilasi, ia berjalan", tapi itu sebabnya obsesi pengujian tumbuh dari dunia bahasa yang dinamis.
clacke
1
@clacke pada dasarnya, terlalu mahal untuk menegakkan tipe saat runtime hanya karena SEMUA harus menjadi objek (untuk memetakan dari string ke jenis apa pun yang mungkin), dan terlalu nyaman untuk tidak memiliki ducktyping karena ducktyping memungkinkan teknik prototipe yang sangat kuat yang mengatasi hal-hal yang biasanya sangat sulit dilakukan dengan antarmuka yang kaku. Selain itu, bahasa statis apa pun menghadapi titik di mana ia perlu membuat pengetikan bebek melalui perpustakaan dinamis, evaluasi dan pengetatan, atau antarmuka, dan hal-hal ini pada dasarnya tidak membuatnya jahat, hanya sangat kuat.
Dmitry
12
isinstance(o, str)

Tautan ke dokumen

Alexander Kojevnikov
sumber
1
Meskipun tautan ini dapat menjawab pertanyaan, lebih baik untuk memasukkan bagian-bagian penting dari jawaban di sini dan memberikan tautan untuk referensi. Jawaban hanya tautan dapat menjadi tidak valid jika halaman tertaut berubah.
EKons
7

Saya pikir hal keren tentang menggunakan bahasa dinamis seperti Python adalah Anda benar-benar tidak perlu memeriksa sesuatu seperti itu.

Saya hanya akan memanggil metode yang diperlukan pada objek Anda dan menangkap AttributeError. Kemudian ini akan memungkinkan Anda untuk memanggil metode Anda dengan objek lain (yang tampaknya tidak terkait) untuk menyelesaikan tugas yang berbeda, seperti mengejek objek untuk pengujian.

Saya sering menggunakan ini ketika mengambil data dari web urllib2.urlopen()yang mengembalikan file seperti objek. Ini pada gilirannya dapat diteruskan ke hampir semua metode yang membaca dari suatu file, karena itu mengimplementasikan read()metode yang sama seperti file nyata.

Tapi saya yakin ada waktu dan tempat untuk menggunakan isinstance(), kalau tidak mungkin tidak akan ada :)

Will Harding
sumber
Contoh yang baik kapan Anda harus menggunakannya adalah jika Anda menguraikan objek json dinamis. Anda tidak tahu sebelumnya apakah bidang adalah string atau kamus.
Gray
6

Untuk validasi jenis yang lebih kompleks, saya suka pendekatan pengesahan typeguard berdasarkan anotasi petunjuk jenis python:

from typeguard import check_type
from typing import List

try:
    check_type('mylist', [1, 2], List[int])
except TypeError as e:
    print(e)

Anda dapat melakukan validasi yang sangat kompleks dengan cara yang sangat bersih dan mudah dibaca.

check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) 
Granitosaurus
sumber
6

Anda dapat memeriksa jenis variabel menggunakan __name__ jenis.

Ex:

>>> a = [1,2,3,4]  
>>> b = 1  
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'
Yerramsetty Rohit
sumber
Terima kasih, ini adalah kode rahasia yang saya inginkan ketika saya menampilkannya sebagai umpan balik kepada pengguna. Butuh waktu terlalu lama untuk menemukan ini ...
Aaron D. Marasco
5

Kepada Hugo:

Mungkin Anda maksudkan listbukan array, tetapi itu menunjuk ke seluruh masalah dengan pengecekan tipe - Anda tidak ingin tahu apakah objek yang dimaksud adalah daftar, Anda ingin tahu apakah itu semacam urutan atau apakah itu objek tunggal. Jadi cobalah untuk menggunakannya seperti urutan.

Katakanlah Anda ingin menambahkan objek ke urutan yang ada, atau jika itu adalah urutan objek, tambahkan semuanya

try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

Salah satu trik dengan ini adalah jika Anda bekerja dengan string dan / atau urutan string - itu rumit, karena string sering dianggap sebagai objek tunggal, tetapi juga urutan karakter. Lebih buruk dari itu, karena itu benar-benar rangkaian string panjang tunggal.

Saya biasanya memilih untuk mendesain API saya sehingga hanya menerima nilai tunggal atau urutan - itu membuat segalanya lebih mudah. Tidak sulit untuk menempatkan [ ]nilai tunggal Anda ketika Anda meneruskannya jika perlu.

(Meskipun ini dapat menyebabkan kesalahan dengan string, karena mereka memang terlihat seperti urutan.)

Chris Barker
sumber
0

Cara sederhana untuk memeriksa jenis adalah membandingkannya dengan sesuatu yang jenisnya Anda ketahui.

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True
Ultrablendz
sumber
-1

Saya pikir cara terbaik adalah mengetik variabel Anda dengan baik. Anda dapat melakukan ini dengan menggunakan pustaka "mengetik".

Contoh:

from typing import NewType UserId = NewType ('UserId', int) some_id = UserId (524313) `

Lihat https://docs.python.org/3/library/typing.html

cherah30
sumber
-7

Anda dapat memeriksa dengan baris di bawah ini untuk memeriksa tipe karakter mana nilai yang diberikan adalah:

def chr_type(chrx):
    if chrx.isalpha()==True:
        return 'alpha'
    elif chrx.isdigit()==True:
        return 'numeric'
    else:
        return 'nothing'

chr_type("12)
Venkatesan
sumber
3
Anda yakin tidak ingin menghapus jawaban ini @ Verkatesan?
Gray