Apa perbedaan antara tipe () dan isinstance ()?

1249

Apa perbedaan antara kedua fragmen kode ini?

Menggunakan type():

import types

if type(a) is types.DictType:
    do_something()
if type(b) in types.StringTypes:
    do_something_else()

Menggunakan isinstance():

if isinstance(a, dict):
    do_something()
if isinstance(b, str) or isinstance(b, unicode):
    do_something_else()
kepala biara
sumber
Catatan: Jika tidak strdan unicode(tempat Anda dapat memeriksa basestring), Anda dapat menggunakan tuple untuk memeriksa beberapa tipe. Untuk memeriksa apakah somethingada intatau strdigunakan isinstance(something, (int, str)).
xuiqzy

Jawaban:

1271

Untuk meringkas isi dari jawaban lain (sudah bagus!), isinstanceMelayani warisan (turunan dari kelas turunan juga merupakan turunan dari kelas dasar), sambil memeriksa persamaan typetidak (menuntut identitas tipe dan menolak instance) subtipe, subclass AKA).

Biasanya, dengan Python, Anda ingin kode Anda mendukung warisan, tentu saja (karena warisan sangat berguna, akan sangat buruk untuk menghentikan kode menggunakan Anda dari menggunakannya!), Jadi isinstancelebih buruk daripada memeriksa identitas types karena mendukung mulus warisan.

Ini bukan berarti bahwa isinstanceadalah baik , pikiran Anda-itu hanya kurang buruk daripada memeriksa kesetaraan jenis. Solusi normal, Pythonic, yang disukai hampir selalu "mengetik bebek": coba gunakan argumen seolah-olah itu dari jenis yang diinginkan, lakukan dalam try/ exceptpernyataan menangkap semua pengecualian yang dapat muncul jika argumen itu tidak sebenarnya ketik (atau jenis lain dengan baik meniru-nya ;-), dan dalam exceptklausa, coba sesuatu yang lain (menggunakan argumen "seolah-olah" itu dari jenis lain).

basestring adalah , bagaimanapun, cukup kasus-tipe builtin khusus yang ada hanya untuk membiarkan Anda menggunakan isinstance(baik strdan unicodesubclass basestring). String adalah urutan (Anda dapat mengulanginya, mengindeksnya, mengirisnya, ...), tetapi Anda umumnya ingin memperlakukannya sebagai tipe "skalar" —rasanya agak tidak menentu (tetapi kasus penggunaan yang cukup sering) untuk menangani semua jenis string (dan mungkin jenis skalar lainnya, yaitu yang tidak dapat Anda lewati) dengan satu cara, semua wadah (daftar, set, dicts, ...) dengan cara lain, dan basestringplus isinstancemembantu Anda melakukan itu — struktur keseluruhan dari ini idiom adalah sesuatu seperti:

if isinstance(x, basestring)
  return treatasscalar(x)
try:
  return treatasiter(iter(x))
except TypeError:
  return treatasscalar(x)

Anda bisa mengatakan bahwa itu basestringadalah Kelas Dasar Abstrak ("ABC") - ia tidak menawarkan fungsionalitas konkret untuk subkelas, melainkan ada sebagai "penanda", terutama untuk digunakan bersama isinstance. Konsep ini jelas berkembang di Python, karena PEP 3119 , yang memperkenalkan generalisasi, diterima dan telah diterapkan mulai dengan Python 2.6 dan 3.0.

PEP memperjelas bahwa, meskipun ABC sering dapat menggantikan mengetik bebek, umumnya tidak ada tekanan besar untuk melakukan itu (lihat di sini ). Namun, ABC yang diterapkan dalam versi Python terbaru menawarkan barang tambahan: isinstance(dan issubclass) sekarang dapat berarti lebih dari sekadar "[turunan dari] kelas turunan" (khususnya, kelas apa pun dapat "didaftarkan" dengan ABC sehingga akan ditampilkan sebagai subkelas, dan instansnya sebagai instance dari ABC); dan ABC juga dapat menawarkan kenyamanan ekstra untuk subclass aktual dengan cara yang sangat alami melalui aplikasi pola desain Metode Templat (lihat di sini dan di sini [[bagian II]] untuk lebih lanjut tentang TM DP, secara umum dan khusus dalam Python, independen dari ABC) .

Untuk mekanisme yang mendasari dukungan ABC seperti yang ditawarkan dalam Python 2.6, lihat di sini ; untuk versi 3.1 mereka, sangat mirip, lihat di sini . Di kedua versi, koleksi modul perpustakaan standar (itulah versi 3.1 — untuk versi 2.6 yang sangat mirip, lihat di sini ) menawarkan beberapa ABC yang bermanfaat.

Untuk keperluan jawaban ini, hal utama untuk mempertahankan tentang ABC (di luar penempatan yang lebih alami untuk fungsi TM DP, dibandingkan dengan alternatif klasik Python dari kelas mixin seperti UserDict.DictMixin ) adalah mereka membuat isinstance(dan issubclass) lebih banyak menarik dan meresap (dalam Python 2.6 dan melangkah maju) daripada sebelumnya (dalam 2.5 dan sebelumnya), dan oleh karena itu, sebaliknya, menjadikan pemeriksaan kesetaraan jenis praktik yang lebih buruk dalam versi Python terbaru daripada yang sudah pernah ada sebelumnya.

Alex Martelli
sumber
9
“Bukan berarti keuangan itu baik, ingatlah — itu hanya lebih buruk daripada memeriksa persamaan jenis. Solusi normal, Pythonic, lebih disukai hampir selalu "mengetik bebek" 'Ini adalah pandangan yang agak terbatas: ada kasus yang sangat baik untuk menggunakan isinstance () dalam, katakanlah, seorang juru bahasa di mana jenis mencerminkan tata bahasa. Menjadi "Pythonic" bukanlah segalanya!
Gene Callahan
2
basestring tidak tersedia dalam Python 3.
erobertc
@GeneCallahan, karena ada kasus yang sangat bagus, tidak berarti apa yang dikatakan bukan aturan umum yang baik. Saya setuju bahwa memeriksa jenis sebelumnya pasti memiliki tempatnya tetapi membiarkan bebek dukun harus mencakup sebagian besar kasus lebih fleksibel dan efisien.
Eric Ed Lohmar
@ erobertc, menurut What's New in Python 3.0 , "Tipe abstrak dasar yang terpasang di dalamnya telah dihapus. Gunakan str sebagai gantinya."
neurite
345

Berikut adalah contoh di mana isinstancemencapai sesuatu yang typetidak bisa:

class Vehicle:
    pass

class Truck(Vehicle):
    pass

dalam hal ini, objek truk adalah Kendaraan, tetapi Anda akan mendapatkan ini:

isinstance(Vehicle(), Vehicle)  # returns True
type(Vehicle()) == Vehicle      # returns True
isinstance(Truck(), Vehicle)    # returns True
type(Truck()) == Vehicle        # returns False, and this probably won't be what you want.

Dengan kata lain, isinstanceberlaku juga untuk subkelas.

Lihat juga: Bagaimana membandingkan jenis objek dengan Python?

Peter
sumber
143
karena ada kasus di mana Anda tidak ingin perilaku isInstance saya berpendapat bahwa tidak ada "lebih baik". Mereka hanya melakukan sesuatu yang berbeda.
philgo20
27
-1, karena "isinstance lebih baik daripada tipe" adalah komentar yang menyesatkan. itu dipahami seperti " typesudah usang, gunakan isinstancesebagai gantinya" pada pandangan pertama. misalnya, apa yang saya inginkan sebenarnya adalah type()pengecekan, tetapi saya disesatkan untuk waktu yang singkat (dan harus melakukan debug sedikit) karena alasan itu.
ceremcem
8
Ini adalah contoh yang baik tentang bagaimana mereka bekerja secara berbeda, tetapi saya hanya menemukan sebuah kasus di mana saya secara spesifik membutuhkan type()dan tidak isinstance(). Seseorang tidak lebih baik; mereka untuk hal yang berbeda.
EL_DON
103

Perbedaan antara isinstance()dan type()dalam Python?

Ketik memeriksa dengan

isinstance(obj, Base)

memungkinkan untuk instance dari subclass dan beberapa basis yang mungkin:

isinstance(obj, (Base1, Base2))

sedangkan jenis-memeriksa dengan

type(obj) is Base

hanya mendukung jenis yang dirujuk.


Sebagai seorang sidenote, iskemungkinan lebih tepat daripada

type(obj) == Base

karena kelas adalah lajang.

Hindari pengecekan tipe - gunakan Polimorfisme (ketikan bebek)

Dengan Python, biasanya Anda ingin mengizinkan jenis apa pun untuk argumen Anda, memperlakukannya seperti yang diharapkan, dan jika objek tidak berperilaku seperti yang diharapkan, itu akan menimbulkan kesalahan yang sesuai. Ini dikenal sebagai polimorfisme, juga dikenal sebagai bebek-mengetik.

def function_of_duck(duck):
    duck.quack()
    duck.swim()

Jika kode di atas berfungsi, kita dapat beranggapan argumen kita adalah bebek. Dengan demikian kita dapat melewati hal-hal lain yang merupakan sub-jenis bebek yang sebenarnya:

function_of_duck(mallard)

atau yang berfungsi seperti bebek:

function_of_duck(object_that_quacks_and_swims_like_a_duck)

dan kode kami masih berfungsi.

Namun, ada beberapa kasus di mana diinginkan untuk secara eksplisit mengetik-cek. Mungkin Anda memiliki hal-hal yang masuk akal untuk dilakukan dengan berbagai jenis objek. Sebagai contoh, objek Dataframe Pandas dapat dibangun dari dikte atau catatan. Dalam kasus seperti itu, kode Anda perlu mengetahui jenis argumen apa yang diperolehnya sehingga dapat menanganinya dengan benar.

Jadi, untuk menjawab pertanyaan:

Perbedaan antara isinstance()dan type()dalam Python?

Izinkan saya menunjukkan perbedaannya:

type

Katakanlah Anda perlu memastikan perilaku tertentu jika fungsi Anda mendapatkan jenis argumen tertentu (kasus penggunaan umum untuk konstruktor). Jika Anda memeriksa jenis seperti ini:

def foo(data):
    '''accepts a dict to construct something, string support in future'''
    if type(data) is not dict:
        # we're only going to test for dicts for now
        raise ValueError('only dicts are supported for now')

Jika kita mencoba untuk mengirimkan dict yang merupakan subkelas dari dict(sebagaimana seharusnya kita bisa, jika kita mengharapkan kode kita untuk mengikuti prinsip Substitusi Liskov , subtipe dapat diganti dengan jenis) kode kita rusak !:

from collections import OrderedDict

foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))

menimbulkan kesalahan!

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in foo
ValueError: argument must be a dict

isinstance

Tetapi jika kita menggunakan isinstance, kita dapat mendukung Substitusi Liskov !:

def foo(a_dict):
    if not isinstance(a_dict, dict):
        raise ValueError('argument must be a dict')
    return a_dict

foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))

kembali OrderedDict([('foo', 'bar'), ('fizz', 'buzz')])

Abstrak Kelas Dasar

Bahkan, kita bisa melakukan yang lebih baik lagi. collectionsmenyediakan Kelas Dasar Abstrak yang memberlakukan protokol minimal untuk berbagai jenis. Dalam kasus kami, jika kami hanya mengharapkan Mappingprotokol, kami dapat melakukan hal berikut, dan kode kami menjadi lebih fleksibel:

from collections import Mapping

def foo(a_dict):
    if not isinstance(a_dict, Mapping):
        raise ValueError('argument must be a dict')
    return a_dict

Tanggapan terhadap komentar:

Perlu dicatat bahwa tipe dapat digunakan untuk memeriksa beberapa kelas yang menggunakan type(obj) in (A, B, C)

Ya, Anda bisa menguji kesetaraan jenis, tetapi alih-alih yang di atas, gunakan beberapa pangkalan untuk aliran kontrol, kecuali Anda secara khusus hanya mengizinkan jenis-jenis itu:

isinstance(obj, (A, B, C))

Perbedaannya, sekali lagi, adalah yang isinstancemendukung subclass yang dapat diganti untuk orang tua tanpa melanggar program, properti yang dikenal sebagai substitusi Liskov.

Bahkan lebih baik lagi, balikkan dependensi Anda dan jangan periksa untuk tipe tertentu sama sekali.

Kesimpulan

Jadi karena kami ingin mendukung penggantian subclass, dalam kebanyakan kasus, kami ingin menghindari pemeriksaan typetipe dan lebih memilih pemeriksaan tipe isinstance- kecuali Anda benar-benar perlu mengetahui kelas yang tepat dari sebuah instance.

Aaron Hall
sumber
Jika Anda memiliki your_module.py di mana Anda memeriksa isinstance(instance, y)dan menggunakan from v.w.x import y, dan Anda mengimpor cek itu, tetapi ketika Anda instantiate instanceAnda menggunakan from x import yalih-alih bagaimana Anda diimpor di your_module.py, pemeriksaan status akan gagal, meskipun itu kelas yang sama.
toonarmycaptain
64

Yang terakhir lebih disukai, karena akan menangani subclass dengan benar. Bahkan, contoh Anda dapat ditulis lebih mudah karena isinstance()parameter kedua mungkin tupel:

if isinstance(b, (str, unicode)):
    do_something_else()

atau, menggunakan basestringkelas abstrak:

if isinstance(b, basestring):
    do_something_else()
John Millikin
sumber
9

Perbedaan penggunaan praktis adalah bagaimana mereka menangani booleans:

Truedan Falsehanya kata kunci yang berarti 1dan 0dengan python. Jadi,

isinstance(True, int)

dan

isinstance(False, int)

keduanya kembali True. Kedua boolean adalah turunan dari bilangan bulat. type()Namun, lebih pintar:

type(True) == int

kembali False.

Alec Alameddine
sumber
0

Untuk perbedaan nyata, kita dapat menemukannya di code, tetapi saya tidak dapat menemukan penerapan perilaku default dari isinstance().

Namun kita bisa mendapatkan yang serupa abc .__ instancecheck__ menurut __instancecheck__ .

Dari atas abc.__instancecheck__, setelah menggunakan tes di bawah ini:

# file tree
# /test/__init__.py
# /test/aaa/__init__.py
# /test/aaa/aa.py
class b():
pass

# /test/aaa/a.py
import sys
sys.path.append('/test')

from aaa.aa import b
from aa import b as c

d = b()

print(b, c, d.__class__)
for i in [b, c, object]:
    print(i, '__subclasses__',  i.__subclasses__())
    print(i, '__mro__', i.__mro__)
    print(i, '__subclasshook__', i.__subclasshook__(d.__class__))
    print(i, '__subclasshook__', i.__subclasshook__(type(d)))
print(isinstance(d, b))
print(isinstance(d, c))

<class 'aaa.aa.b'> <class 'aa.b'> <class 'aaa.aa.b'>
<class 'aaa.aa.b'> __subclasses__ []
<class 'aaa.aa.b'> __mro__ (<class 'aaa.aa.b'>, <class 'object'>)
<class 'aaa.aa.b'> __subclasshook__ NotImplemented
<class 'aaa.aa.b'> __subclasshook__ NotImplemented
<class 'aa.b'> __subclasses__ []
<class 'aa.b'> __mro__ (<class 'aa.b'>, <class 'object'>)
<class 'aa.b'> __subclasshook__ NotImplemented
<class 'aa.b'> __subclasshook__ NotImplemented
<class 'object'> __subclasses__ [..., <class 'aaa.aa.b'>, <class 'aa.b'>]
<class 'object'> __mro__ (<class 'object'>,)
<class 'object'> __subclasshook__ NotImplemented
<class 'object'> __subclasshook__ NotImplemented
True
False

Saya mendapatkan kesimpulan ini, Untuk type:

# according to `abc.__instancecheck__`, they are maybe different! I have not found negative one 
type(INSTANCE) ~= INSTANCE.__class__
type(CLASS) ~= CLASS.__class__

Untuk isinstance:

# guess from `abc.__instancecheck__`
return any(c in cls.__mro__ or c in cls.__subclasses__ or cls.__subclasshook__(c) for c in {INSTANCE.__class__, type(INSTANCE)})

BTW: lebih baik tidak mencampur penggunaan relative and absolutely import, gunakan absolutely importdari project_dir (ditambahkan oleh sys.path)

Cheney
sumber