Quali sono le differenze tra type () e isinstance ()?


1249

Quali sono le differenze tra questi due frammenti di codice?

Utilizzando type():

import types

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

Utilizzando isinstance():

if isinstance(a, dict):
    do_something()
if isinstance(b, str) or isinstance(b, unicode):
    do_something_else()

Nota: se non lo è stre unicode(dove puoi semplicemente controllare basestring), puoi usare una tupla per controllare più tipi. Per verificare se lo somethingè into strutilizzare isinstance(something, (int, str)).
xuiqzy,

Risposte:


1271

Per riassumere il contenuto di altre risposte (già buone!), isinstanceProvvede all'ereditarietà (anche un'istanza di una classe derivata è un'istanza di una classe di base), mentre verifica l'uguaglianza di typenon (richiede l'identità dei tipi e rifiuta le istanze di sottotipi, sottoclassi AKA).

Normalmente, in Python, vuoi che il tuo codice supporti l'ereditarietà, ovviamente (poiché l'ereditarietà è così utile, sarebbe male impedire al codice di usare il tuo dall'usarlo!), Quindi isinstanceè meno male che controllare l'identità di types perché supporta perfettamente eredità.

Non isinstanceè un bene , intendiamoci, è solo meno male che controllare l'uguaglianza dei tipi. La normale soluzione preferita di Pythonic è quasi sempre la "tipizzazione anatra": prova a usare l'argomento come se fosse di un certo tipo desiderato, fallo in un'istruzione try/ exceptcatturando tutte le eccezioni che potrebbero sorgere se l'argomento non fosse in realtà quello digitare (o qualsiasi altro tipo piacevolmente da imitazione di anatra ;-), e nella exceptclausola, provare qualcos'altro (usando l'argomento "come se" fosse di qualche altro tipo).

basestring è , tuttavia, un caso piuttosto speciale: un tipo incorporato che esiste solo per permetterti di usare isinstance(entrambi stre unicodesottoclasse basestring). Le stringhe sono sequenze (è possibile eseguire il ciclo su di esse, indicizzarle, dividerle, ...), ma in genere si desidera trattarle come tipi "scalari": è in qualche modo inconsistente (ma un caso d'uso ragionevolmente frequente) trattare tutti i tipi di stringhe (e forse altri tipi scalari, ovvero quelli su cui non è possibile eseguire il loop) in un modo, tutti i contenitori (elenchi, set, dicts, ...) in un altro modo e in basestringpiù isinstanceti aiuta a farlo — la struttura generale di questo idioma è qualcosa di simile:

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

Si potrebbe dire che si basestringtratta di una classe base astratta ("ABC") - non offre funzionalità concrete alle sottoclassi, ma esiste piuttosto come un "marcatore", principalmente per l'uso con isinstance. Il concetto è ovviamente in crescita in Python, dal PEP 3119 , che ne introduce una generalizzazione, è stato accettato ed è stato implementato a partire da Python 2.6 e 3.0.

Il PEP chiarisce che, mentre gli ABC possono spesso sostituire la tipizzazione delle anatre, generalmente non esiste una grande pressione per farlo (vedi qui ). Gli ABC come implementati nelle recenti versioni di Python offrono tuttavia extra extra: isinstance(e issubclass) ora possono significare più di un semplice "[un'istanza di] una classe derivata" (in particolare, qualsiasi classe può essere "registrata" con un ABC in modo che possa mostra come sottoclasse e le sue istanze come istanze della ABC); e gli ABC possono anche offrire ulteriore comodità alle sottoclassi effettive in modo molto naturale tramite le applicazioni del modello di progettazione del metodo Template (vedere qui e qui [[parte II]] per ulteriori informazioni su TM DP, in generale e in particolare su Python, indipendentemente dagli ABC) .

Per i meccanismi di base del supporto ABC offerti in Python 2.6, vedere qui ; per la loro versione 3.1, molto simile, vedi qui . In entrambe le versioni, le raccolte di moduli di libreria standard (che è la versione 3.1, per la versione 2.6 molto simile, vedi qui ) offrono diversi ABC utili.

Ai fini di questa risposta, la cosa fondamentale da mantenere sugli ABC (oltre a un posizionamento probabilmente più naturale per la funzionalità TM DP, rispetto alla classica alternativa Python di classi di mixin come UserDict.DictMixin ) è che fanno isinstance(e issubclass) molto di più attraente e pervasivo (in Python 2.6 e successivi) rispetto al passato (in 2.5 e precedenti), e quindi, al contrario, rende il controllo dell'uguaglianza dei tipi una pratica ancora peggiore nelle recenti versioni di Python di quanto non fosse in passato.


9
'Non è che l'istanza sia buona, intendiamoci, è solo meno male che controllare l'uguaglianza dei tipi. La normale soluzione preferita di Pythonic è quasi sempre la "tipizzazione anatra". Questa è una visione piuttosto limitata: ci sono casi molto buoni per usare isinstance () in, per esempio, un interprete in cui i tipi riflettono la grammatica. Essere "Pythonic" non è tutto!
Gene Callahan,

2
basestring non è disponibile in Python 3.
erobertc

@GeneCallahan, perché ci sono casi molto buoni, non significa che ciò che è stato detto non sia una buona regola generale. Concordo sul fatto che controllare il tipo in anticipo abbia sicuramente il suo posto, ma lasciare che il ciarlatano delle anatre dovrebbe coprire la maggior parte dei casi in modo più flessibile ed efficiente.
Eric Ed Lohmar,

@erobertc, secondo le novità di Python 3.0 , "Il tipo astratto basestring incorporato è stato rimosso. Usa invece str."
neurite

345

Ecco un esempio in cui si isinstanceottiene qualcosa che typenon può:

class Vehicle:
    pass

class Truck(Vehicle):
    pass

in questo caso, un oggetto camion è un Veicolo, ma otterrai questo:

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.

In altre parole, isinstancevale anche per le sottoclassi.

Vedi anche: Come confrontare il tipo di un oggetto in Python?


143
perché c'è un caso in cui non si desidera il comportamento isInstance, direi che non esiste un "migliore". Fanno solo qualcosa di diverso.
philgo20

27
-1, perché "isinstance is better than type" è un commento fuorviante. è inteso come " typeè deprecato, usa isinstanceinvece" a prima vista. per esempio, quello che volevo era esattamente il type()controllo, ma sono stato ingannato per un breve periodo (e ho dovuto eseguire il debug un po ') per questo motivo.
ceremcem,

8
È un buon esempio di come funzionano in modo diverso, ma ho appena incontrato un caso in cui avevo specificamente bisogno type()e non isinstance(). Uno non è migliore; sono per cose diverse.
EL_DON

103

Differenze tra isinstance()e type()in Python?

Verifica del tipo con

isinstance(obj, Base)

consente istanze di sottoclassi e più possibili basi:

isinstance(obj, (Base1, Base2))

mentre il controllo del tipo con

type(obj) is Base

supporta solo il tipo a cui si fa riferimento.


Come sidenote, isè probabilmente più appropriato di

type(obj) == Base

perché le lezioni sono singleton.

Evita il controllo del tipo: usa il polimorfismo (duck-typing)

In Python, di solito si desidera consentire qualsiasi tipo per i propri argomenti, trattarlo come previsto e se l'oggetto non si comporta come previsto, genererà un errore appropriato. Questo è noto come polimorfismo, noto anche come dattilografia.

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

Se il codice sopra funziona, possiamo presumere che il nostro argomento sia un'anatra. Quindi possiamo passare ad altre cose che sono veri e propri sottotipi di anatre:

function_of_duck(mallard)

o che funzionano come un'anatra:

function_of_duck(object_that_quacks_and_swims_like_a_duck)

e il nostro codice funziona ancora.

Tuttavia, ci sono alcuni casi in cui è auspicabile un controllo esplicito del tipo. Forse hai cose sensate da fare con diversi tipi di oggetti. Ad esempio, l'oggetto Pandas Dataframe può essere costruito da dicts o record. In tal caso, il codice deve conoscere il tipo di argomento che sta ricevendo per poterlo gestire correttamente.

Quindi, per rispondere alla domanda:

Differenze tra isinstance()e type()in Python?

Mi permetta di dimostrare la differenza:

type

Supponi di dover garantire un determinato comportamento se la tua funzione ottiene un certo tipo di argomento (un caso d'uso comune per i costruttori). Se controlli il tipo in questo modo:

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

Se proviamo a passare un dict che è una sottoclasse di dict(come dovremmo essere in grado, se ci aspettiamo che il nostro codice segua il principio di Liskov Substitution , che i sottotipi possano essere sostituiti per tipi) il nostro codice si rompe !:

from collections import OrderedDict

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

genera un errore!

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

isinstance

Ma se lo utilizziamo isinstance, possiamo supportare la sostituzione di 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')]))

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

Classi di base astratte

In effetti, possiamo fare ancora meglio. collectionsfornisce classi di base astratte che applicano protocolli minimi per vari tipi. Nel nostro caso, se ci aspettiamo solo il Mappingprotocollo, possiamo fare quanto segue e il nostro codice diventa ancora più flessibile:

from collections import Mapping

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

Risposta al commento:

Va notato che il tipo può essere utilizzato per verificare più classi utilizzando type(obj) in (A, B, C)

Sì, è possibile verificare l'uguaglianza dei tipi, ma invece di quanto sopra, utilizzare le basi multiple per il flusso di controllo, a meno che non si consenta specificamente solo quei tipi:

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

La differenza, ancora una volta, è che isinstancesupporta le sottoclassi che possono essere sostituite al genitore senza altrimenti interrompere il programma, una proprietà nota come sostituzione di Liskov.

Ancora meglio, però, inverti le tue dipendenze e non controllare affatto tipi specifici.

Conclusione

Quindi, poiché vogliamo supportare la sostituzione di sottoclassi, nella maggior parte dei casi, vogliamo evitare il controllo del tipo con typee preferiamo il controllo del tipo con isinstance- a meno che tu non abbia davvero bisogno di conoscere la classe precisa di un'istanza.


Se hai your_module.py dove controlli isinstance(instance, y)e usi e importi from v.w.x import yquel controllo, ma quando crei un'istanza instanceusi from x import yinvece di come y è stato importato in your_module.py, il controllo isinstance fallirà, anche se è la stessa classe.
toonarmycaptain

64

Quest'ultimo è preferito, perché gestirà correttamente le sottoclassi. In effetti, il tuo esempio può essere scritto ancora più facilmente perché isinstance()il secondo parametro può essere una tupla:

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

oppure, usando la basestringclasse astratta:

if isinstance(b, basestring):
    do_something_else()


9

Una differenza pratica nell'utilizzo è come gestiscono booleans:

Truee Falsesono solo parole chiave che significano 1e 0in Python. Così,

isinstance(True, int)

e

isinstance(False, int)

entrambi ritornano True. Entrambi i booleani sono un'istanza di un numero intero. type(), tuttavia, è più intelligente:

type(True) == int

ritorna False.


0

Per le vere differenze, possiamo trovarlo in code, ma non riesco a trovare l'implementazione del comportamento predefinito diisinstance() .

Tuttavia possiamo ottenere quello simile abc .__ instancecheck__ secondo __instancecheck__ .

Dall'alto abc.__instancecheck__, dopo aver usato il test di seguito:

# 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

Ottengo questa conclusione, per type:

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

Per 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)})

A proposito: meglio non mescolare l'uso relative and absolutely import, usare absolutely importda project_dir (aggiunto da sys.path)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.