Pertanyaan Apa perbedaan antara type () dan isinstance ()?


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()

933
2017-10-11 03:50


asal


Jawaban:


Untuk meringkas isi jawaban lainnya (sudah bagus!), isinstance melayani pewarisan (turunan dari kelas turunan adalah contoh kelas dasar, juga), sambil memeriksa persamaan type tidak (itu menuntut identitas jenis dan menolak contoh subtipe, subclass AKA).

Biasanya, dengan Python, Anda ingin kode Anda untuk mendukung pewarisan, tentu saja (karena warisan sangat berguna, itu akan menjadi buruk untuk menghentikan kode menggunakan Anda menggunakannya!), Jadi isinstance kurang buruk daripada memeriksa identitas typekarena mendukung warisan dengan lancar.

Bukan itu isinstance aku s baik, ingat itu — hanya saja kurang buruk daripada memeriksa kesetaraan tipe. Solusi yang normal, Pythonic, lebih disukai hampir selalu "bebek mengetik": coba gunakan argumen seolah-olah itu adalah tipe tertentu yang diinginkan, lakukan dalam a try/except pernyataan menangkap semua pengecualian yang bisa timbul jika argumen itu tidak pada kenyataannya dari jenis itu (atau jenis lain dengan baik bebek menirukannya ;-), dan di except klausa, coba sesuatu yang lain (menggunakan argumen "seolah-olah" itu dari beberapa jenis lainnya).

basestring  aku sNamun, cukup kasus khusus - jenis bawaan yang ada hanya untuk membiarkan Anda gunakan isinstance (kedua str dan unicode subkelas basestring). String adalah sekuens (Anda dapat mengulanginya, mengindeksnya, mengirisnya, ...), tetapi Anda biasanya ingin memperlakukannya sebagai jenis "skalar" - itu agak inklusif (tapi cukup sering digunakan) untuk mengobati semua jenis string (dan mungkin jenis skalar lainnya, yaitu, yang tidak dapat Anda lingkari) dengan satu cara, semua kontainer (daftar, set, dicts, ...) dengan cara lain, dan basestring plus isinstance membantu Anda melakukan itu — struktur keseluruhan idiom ini adalah sesuatu seperti:

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

Anda bisa mengatakan itu basestring adalah Kelas Basis Abstrak ("ABC") - tidak menawarkan fungsionalitas konkret untuk subclass, melainkan sebagai "penanda", terutama untuk digunakan dengan isinstance. Konsep ini jelas berkembang dalam Python, sejak PEP 3119, yang memperkenalkan generalisasi itu, diterima dan telah dilaksanakan mulai dengan Python 2.6 dan 3.0.

PEP memperjelas bahwa, sementara ABC sering dapat menggantikan mengetik bebek, pada umumnya tidak ada tekanan besar untuk melakukannya (lihat sini). ABC seperti yang diterapkan dalam versi Python terbaru memang menawarkan barang ekstra: isinstance (dan issubclass) sekarang dapat berarti lebih dari sekedar "[sebuah instance dari] kelas turunan" (khususnya, setiap kelas dapat "terdaftar" dengan ABC sehingga akan ditampilkan sebagai subkelas, dan contohnya sebagai contoh dari ABC); dan ABC juga dapat menawarkan kenyamanan ekstra untuk subclass yang sebenarnya dengan cara yang sangat alami melalui aplikasi pola desain Template Metode (lihat sini dan sini [[bagian II]] untuk lebih banyak tentang DP TM, secara umum dan khusus dalam Python, tidak tergantung pada ABC).

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

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


988
2017-10-11 04:31



Inilah alasannya isinstance lebih baik dari type:

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, isinstance juga berlaku untuk subclass.

Juga lihat: Bagaimana cara membandingkan jenis objek dengan Python?


260
2017-10-11 03:58



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

Ketik-centang dengan

isinstance(obj, Base)

memungkinkan untuk instance subclass dan beberapa kemungkinan basis:

isinstance(obj, (Base1, Base2))

sedangkan tipe-checking dengan

type(obj) is Base

hanya mendukung tipe yang direferensikan.


Sebagai sidenote, is kemungkinan lebih tepat daripada

type(obj) == Base

karena kelas adalah lajang.

Hindari pengecekan jenis - gunakan Polymorphism (bebek-mengetik)

Dalam Python, biasanya Anda ingin mengizinkan jenis apa pun untuk argumen Anda, perlakukan seperti yang diharapkan, dan jika objek tidak berperilaku seperti yang diharapkan, itu akan meningkatkan 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 menganggap argumen kita adalah seekor bebek. Dengan demikian kita dapat mewariskan hal-hal lain adalah sub-jenis bebek yang sebenarnya:

function_of_duck(mallard)

atau yang bekerja seperti bebek:

function_of_duck(object_that_quacks_and_swims_like_a_duck)

dan kode kami masih berfungsi.

Namun, ada beberapa kasus yang diinginkan untuk secara eksplisit diketik. Mungkin Anda memiliki hal-hal yang masuk akal untuk dilakukan dengan jenis objek yang berbeda. Sebagai contoh, objek Pandas Dataframe dapat dikonstruksi dari dicts atau catatan. Dalam kasus seperti itu, kode Anda perlu mengetahui jenis argumen apa yang didapatkan sehingga dapat menanganinya dengan tepat.

Jadi, untuk menjawab pertanyaan:

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

Izinkan saya untuk menunjukkan perbedaannya:

type

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

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

Jika kami mencoba memberikan dikt yang merupakan subkelas dari dict (seperti yang seharusnya kita bisa, jika kita mengharapkan kode kita mengikuti prinsip Pergantian Liskov, subtipe itu dapat digantikan untuk jenis) kode kami rusak !:

from collections import OrderedDict

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

memunculkan 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 gunakan isinstance, kami dapat mendukung Liskov Substitusi !:

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')])

Kelas Basis Abstrak

Bahkan, kita bisa melakukannya dengan lebih baik. collections menyediakan Kelas Basis Abstrak yang menegakkan protokol minimal untuk berbagai jenis. Dalam kasus kami, jika kami hanya mengharapkan Mappingprotokol, kita dapat melakukan hal berikut, dan kode kita 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 untuk berkomentar:

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

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

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

Perbedaannya, sekali lagi, adalah itu isinstance mendukung subclass yang dapat diganti untuk induk tanpa melanggar program, properti yang dikenal sebagai substitusi Liskov.

Bahkan lebih baik, membalikkan dependensi Anda dan tidak memeriksa jenis-jenis tertentu sama sekali.

Kesimpulan

Jadi karena kami ingin mendukung mengganti subclass, dalam banyak kasus, kami ingin menghindari pengecekan tipe type dan lebih suka jenis-memeriksa dengan isinstance - kecuali Anda benar-benar perlu mengetahui kelas instance yang tepat.


60
2018-02-06 04:13



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

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

atau, menggunakan basestring kelas abstrak:

if isinstance(b, basestring):
    do_something_else()

55
2017-10-11 03:54



Menurut dokumentasi python di sini adalah pernyataan:

8.15. types - Nama untuk tipe bawaan

Mulai dengan Python 2.2, built-in   fungsi pabrik seperti int() dan    str() juga nama untuk   jenis yang sesuai.

Begitu isinstance() sebaiknya lebih dipilih type().


11
2017-10-11 03:59



Untuk perbedaan nyata, kita dapat menemukannya 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: sebaiknya tidak mencampur penggunaan relative and absolutely import, gunakan absolutely import dari project_dir (ditambahkan oleh sys.path)


0
2018-05-10 08:00