variable - python3 type




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

Quali sono le differenze tra questi due frammenti di codice? Usando 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()

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

Tipo di controllo con

isinstance(obj, Base)

consente le 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 di riferimento.

Come sidenote, è probabilmente più appropriato di

type(obj) == Base

perché le classi sono singleton.

Evita il controllo dei caratteri - usa il polimorfismo (tipizzazione delle anatre)

In Python, in genere si desidera consentire qualsiasi tipo per gli 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 la nostra argomentazione sia un'anatra. Così possiamo passare in altre cose sono sottotipi effettivi di anatra:

function_of_duck(mallard)

o che funziona 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 è consigliabile esplicitamente il controllo del tipo. Forse hai cose sensate da fare con diversi tipi di oggetti. Ad esempio, l'oggetto Pandora Dataframe può essere costruito da dicts o record. In tal caso, il tuo codice deve sapere quale tipo di argomento sta ottenendo in modo che possa gestirlo correttamente.

Quindi, per rispondere alla domanda:

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

Permettimi di dimostrare la differenza:

type

Supponiamo che sia necessario garantire un determinato comportamento se la 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 in un ditt 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 i tipi) il nostro codice si rompe !:

from collections import OrderedDict

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

solleva 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 usiamo 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 forniscono classi base astratte che applicano protocolli minimi per vari tipi. Nel nostro caso, se ci aspettiamo solo il protocollo di Mapping , 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 usato per verificare contro più classi usando il type(obj) in (A, B, C)

Sì, puoi testare l'uguaglianza dei tipi, ma al posto di quanto sopra, usa le basi multiple per il controllo del flusso, a meno che tu non stia specificatamente permettendo solo quei tipi:

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

La differenza, ancora una volta, è che l' isinstance supporta sottoclassi che possono essere sostituite al genitore senza interrompere il programma, una proprietà nota come sostituzione di Liskov.

Ancora meglio, però, invertire le dipendenze e non controllare affatto i tipi specifici.

Conclusione

Quindi, dal momento che vogliamo supportare la sostituzione delle sottoclassi, nella maggior parte dei casi, vogliamo evitare il controllo dei type con il type e preferiamo il controllo dei isinstance con isinstance , a meno che tu non abbia davvero bisogno di conoscere la classe precisa di un'istanza.


Ecco perché isinstance è migliore del type :

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, isinstance è vero anche per le sottoclassi.

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


Per riassumere il contenuto di altre (già buone!) Risposte, isinstance rivolge all'ereditarietà (un'istanza di una classe derivata è anche un'istanza di una classe base), mentre il controllo dell'eguaglianza di type non lo fa (richiede identità di tipi e rifiuta le istanze di sottotipi, sottoclassi di AKA).

Normalmente, in Python, vuoi che il tuo codice supporti l'ereditarietà, ovviamente (dato che l'ereditarietà è così utile, sarebbe brutto fermare il codice usando il tuo per usarlo!), Quindi l' isinstance è meno cattiva che controllare l'identità di type s perché supporta senza problemi l'ereditarietà.

Non è che l' isinstance sia buona , isinstance è solo meno cattiva che controllare l'uguaglianza dei tipi. La normale soluzione pitonica preferita è quasi invariabilmente la "digitazione anatra": prova ad usare l'argomento come se fosse di un certo tipo desiderato, fallo in una dichiarazione try / except prendendo tutte le eccezioni che potrebbero sorgere se l'argomento non fosse in effetti di quel tipo (o di qualsiasi altro tipo che lo imita bene ;-), e nella clausola except , prova qualcos'altro (usando l'argomento "come se" fosse di un altro tipo).

basestring è , tuttavia, un caso piuttosto particolare, un tipo built-in che esiste solo per consentire l'uso di isinstance (sia str e unicode sottoclasse basestring ). Le stringhe sono sequenze (è possibile eseguirne il looping, indicizzarle, tagliarle, ...), ma in generale si desidera trattarle come tipi "scalari": è piuttosto incoveniente (ma un caso d'uso ragionevolmente frequente) per trattare tutti i tipi di stringhe (e forse altri tipi di scalari, cioè quelli su cui non è possibile eseguire il looping) in un modo, tutti i contenitori (elenchi, insiemi, dict, ...) in un altro modo, e basestring plus isinstance ti 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 basestring è una classe base astratta ("ABC") - non offre alcuna funzionalità concreta per sottoclassi, ma piuttosto esiste come "marcatore", principalmente per l'uso con isinstance . Il concetto è ovviamente in crescita in Python, dal momento che PEP 3119 , che introduce una sua 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 digitazione anatra, generalmente non c'è una grande pressione per farlo (vedi here ). Gli ABC come implementati nelle recenti versioni di Python offrono comunque extra extra: isinstance (e issubclass ) possono ora significare più che "[un'istanza di] una classe derivata" (in particolare, qualsiasi classe può essere "registrata" con un ABC in modo che mostrerà come sottoclasse e le sue istanze come istanze dell'ABC); e gli ABC possono anche offrire una maggiore praticità alle sottoclassi reali in modo molto naturale tramite le applicazioni del modello di progettazione Template Method (vedere here e here [[part II]] per ulteriori informazioni su TM DP, in generale e in particolare su Python, indipendentemente da ABCs) .

Per le meccaniche di base del supporto ABC come offerto in Python 2.6, vedere here ; per la loro versione 3.1, molto simile, vedi here . In entrambe le versioni, le collections moduli di librerie standard (che è la versione 3.1, per la versione 2.6 molto simile, vedi here ) offre diversi ABC utili.

Ai fini di questa risposta, la cosa fondamentale da tenere riguardo agli ABC (oltre a un posizionamento verosimilmente più naturale per la funzionalità di DP DP, rispetto alla classica alternativa a Python delle classi di mixin come UserDict.DictMixin ) è che essi creano isinstance (e issubclass ) molto più attraente e pervasivo (in Python 2.6 e in futuro) rispetto a prima (in 2.5 e prima), e quindi, al contrario, rendere il controllo dell'uguaglianza di tipo una pratica ancora peggiore nelle recenti versioni di Python di quanto non fosse già.


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

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

oppure, usando la classe astratta basestring :

if isinstance(b, basestring):
    do_something_else()




types