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. collections
fornisce classi di base astratte che applicano protocolli minimi per vari tipi. Nel nostro caso, se ci aspettiamo solo il Mapping
protocollo, 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 isinstance
supporta 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 type
e preferiamo il controllo del tipo con isinstance
- a meno che tu non abbia davvero bisogno di conoscere la classe precisa di un'istanza.
str
eunicode
(dove puoi semplicemente controllarebasestring
), puoi usare una tupla per controllare più tipi. Per verificare se losomething
èint
ostr
utilizzareisinstance(something, (int, str))
.