Qual è il modo canonico per controllare il tipo in Python?


1278

Qual è il modo migliore per verificare se un determinato oggetto è di un determinato tipo? Che ne dici di verificare se l'oggetto eredita da un determinato tipo?

Diciamo che ho un oggetto o. Come posso verificare se si tratta di un str?


7
Bene, l'approccio canonico in Python è di non controllare affatto il tipo (a meno che tu non stia eseguendo il debug). Di solito provi ad usarlo come una stringa (es. Concatenato con altre stringhe, stampa su console, ecc.); se pensi che potrebbe fallire, usa try / tranne o hasattr. Detto questo, la risposta accettata è il modo canonico di fare ciò che generalmente "non dovresti fare" nel mondo Python. Per ulteriori informazioni, google "Python duck typing" o leggere questi: voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883/...
Jon Coombs

9
Penso che il signor Coombs stia trascurando esempi come le classi serializzabili non JSON. Se si inserisce un grosso blocco di dati attraverso una funzione (il cui codice non è possibile influenzare) si potrebbe voler convertire alcuni pezzi di quei dati in, ad esempio, in <str> prima di passarli. Almeno è così che sono finito su questa pagina ...
John Carrell,

2
Sembra che la ragione più comune per chiedere questo sia che si voglia distinguere tra stringhe e iterabili di stringhe. Questa è una domanda delicata perché le stringhe sono iterabili di stringhe - una stringa a singolo carattere è persino una sequenza di se stessa (l'ultima volta che ho controllato - probabilmente non si dovrebbe fare affidamento su di essa). Ma qualcuno avrebbe mai usato qualcosa di simile a una stringa? . Quindi la risposta a "Cosa devo fare per distinguere tra stringhe e altri iterabili di stringhe?" è correttamente: "Dipende da cosa stai cercando di fare". MrGreen
clacke,

2
Le annotazioni di tipo Python ora sono una cosa. Dai un'occhiata a Mypy
Sheena,

Risposte:


1522

Per verificare se oè un'istanza stro una sottoclasse di str, utilizzare isinstance (questo sarebbe il modo "canonico"):

if isinstance(o, str):

Per verificare se il tipo di oè esattamente str(escludi le sottoclassi):

if type(o) is str:

Inoltre, funziona e può essere utile in alcuni casi:

if issubclass(type(o), str):

Vedere Funzioni integrate nel Riferimento della libreria Python per informazioni pertinenti.

Un'altra nota: in questo caso, se stai usando Python 2, potresti effettivamente voler usare:

if isinstance(o, basestring):

perché questo catturerà anche stringhe Unicode ( unicodenon è una sottoclasse di str; entrambe stre unicodesono sottoclassi di basestring). Nota che basestringnon esiste più in Python 3, dove esiste una stretta separazione di stringhe ( str) e dati binari ( bytes).

In alternativa, isinstanceaccetta una tupla di classi. Questo restituirà Truese oè un'istanza di una sottoclasse di uno di (str, unicode):

if isinstance(o, (str, unicode)):

31
str .__ sottoclassi __ () restituisce solo le sottoclassi dirette di str e non fa la stessa cosa di issubclass () o isinstance (). (Per fare ciò, dovresti chiamare ricorsivamente .__ sottoclassi __ ().
Thomas Wouters,

15
Questa è una buona risposta, ma penso che dovrebbe davvero iniziare con un avvertimento che di solito non dovresti farlo in Python. Così com'è, sembra convalidare l'assunto che questa è una "cosa canonica da fare in Python", che non lo è.
Jon Coombs,

4
Queste sono le risposte di python2. Ad esempio, non esiste alcun basestring in python3.
dfrankow,

4
Qual è la differenza tra istanza e "esattamente"? Se type(a) is Objectallora non è vero anche quello isinstance(a, Object). Tuttavia, se type(a) is SubClassOfObject, allora type(a) is Object == False, ma isinstance(a, Object) == True. Giusto?
mavavilj,

1
@mavavilj - a is bsignifica che aeb sono esattamente la stessa cosa, cioè riferimenti alla stessa entità in memoria. Quindi ae bdovrebbe essere esattamente la stessa classe, non sottoclassi, come con isinstance(). Vedi ad esempio stackoverflow.com/a/133024/1072212
Terry Brown

196

Il modo più Pythonic per controllare il tipo di un oggetto è ... non controllarlo.

Poiché Python incoraggia Duck Typing , dovresti semplicemente try...exceptusare i metodi dell'oggetto nel modo in cui vuoi usarli. Quindi, se la tua funzione è alla ricerca di un oggetto file scrivibile, non controllare che sia una sottoclasse di file, prova solo a usare il suo .write()metodo!

Certo, a volte queste belle astrazioni si rompono ed isinstance(obj, cls)è quello che ti serve. Ma usa con parsimonia.


75
IMHO, il modo più pitonico è di affrontare qualsiasi argomento che viene dato. Nel mio codice spesso non riesco a sapere se ricevo un oggetto o una matrice di oggetti e utilizzo il controllo del tipo internamente per convertire un singolo oggetto in un elenco di un elemento.
sastanin,

14
Piuttosto quindi solo cercando di usare il suo metodo di scrittura ci sono momenti in cui vuoi farlo senza causare un'eccezione. In questo caso potresti fare ... if hasattr(ob, "write") and callable(ob.write): O salvare un po 'di accesso al dict ...func = getattr(ob, "write", None) if callable(func): ...
ideasman42

142
La digitazione dell'anatra riguarda l' uso di una biblioteca. Il controllo del tipo riguarda la scrittura di una libreria. Non è lo stesso dominio problematico.
Ricky,

16
@RickyA, non sono d'accordo. La digitazione anatra riguarda l'interazione con gli oggetti utilizzando interfacce con semantica ben nota. Ciò può essere applicato al codice della libreria o al codice che utilizza tale libreria.
Dan Lenski,

6
@ nyuszika7h, In Python3 sopprime hasattrsolo un AttributeError - Vedi: docs.python.org/3.4/library/functions.html#hasattr
ideasman42

57

isinstance(o, str)restituirà Trueif oè un stro è di un tipo che eredita da str.

type(o) is strrestituirà Truese e solo se oè uno str. Restituirà Falsese oè di un tipo che eredita da str.


6
Naturalmente, ciò fallirà se l'oggetto non è un'istanza di 'str', ma invece di qualcosa di simile a una stringa. Come unicode, mmap, UserString o qualsiasi altro tipo definito dall'utente. Il solito approccio in Python non è quello di fare i typechecks.
Thomas Wouters,

6
Non devi scusarti, va bene rispondere alla tua domanda. SO è per le risposte, non per il karma.
Eli Bendersky,

2
Questo è molto utile Perché la differenza tra isinstancee type(var) == type('')non è chiara.
sastanin,

30

Dopo che la domanda è stata posta e risposta, i suggerimenti sul tipo sono stati aggiunti a Python . I suggerimenti sul tipo in Python consentono di controllare i tipi, ma in modo molto diverso dai linguaggi tipizzati staticamente. I suggerimenti sul tipo in Python associano i tipi previsti di argomenti alle funzioni come dati accessibili in runtime associati alle funzioni e ciò consente di verificare i tipi. Esempio di sintassi del suggerimento tipo:

def foo(i: int):
    return i

foo(5)
foo('oops')

In questo caso vogliamo che venga attivato un errore foo('oops')poiché è il tipo annotato dell'argomento int. Il suggerimento sul tipo aggiunto non provoca un errore quando lo script viene eseguito normalmente. Tuttavia, aggiunge attributi alla funzione che descrive i tipi previsti che altri programmi possono interrogare e utilizzare per verificare la presenza di errori di tipo.

Uno di questi altri programmi che è possibile utilizzare per trovare l'errore di tipo è mypy:

mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"

(Potrebbe essere necessario installarlo mypydal gestore dei pacchetti. Non credo che venga fornito con CPython ma sembra avere un certo livello di "ufficialità".)

Il controllo del tipo in questo modo è diverso dal controllo del tipo nei linguaggi compilati digitati staticamente. Poiché i tipi sono dinamici in Python, la verifica dei tipi deve essere eseguita in fase di esecuzione, il che comporta un costo, anche su programmi corretti, se insistiamo sul fatto che ciò avvenga in ogni occasione. I controlli espliciti sui tipi possono anche essere più restrittivi del necessario e causare errori non necessari (ad esempio, l'argomento deve davvero essere del listtipo esatto o è qualcosa di assolutamente iterabile?).

L'aspetto positivo del controllo esplicito del tipo è che può rilevare errori in precedenza e fornire messaggi di errore più chiari rispetto alla digitazione duck. I requisiti esatti di un tipo di anatra possono essere espressi solo con documentazione esterna (si spera che sia accurata e accurata) e errori di tipi incompatibili possono verificarsi lontano da dove hanno origine.

I suggerimenti sui tipi di Python hanno lo scopo di offrire un compromesso in cui i tipi possono essere specificati e controllati ma non ci sono costi aggiuntivi durante la normale esecuzione del codice.

Il typingpacchetto offre variabili di tipo che possono essere utilizzate nei suggerimenti di tipo per esprimere i comportamenti necessari senza richiedere tipi particolari. Ad esempio, include variabili come Iterablee Callableper i suggerimenti per specificare la necessità di qualsiasi tipo con tali comportamenti.

Mentre i suggerimenti sul tipo sono il modo più Pythonic per controllare i tipi, spesso è ancora più Pythonic a non controllare affatto i tipi e fare affidamento sulla tipizzazione duck. I suggerimenti sul tipo sono relativamente nuovi e la giuria è ancora fuori quando sono la soluzione più Pythonic. Un confronto relativamente non controverso ma molto generale: i suggerimenti sul tipo forniscono una forma di documentazione che può essere applicata, consente al codice di generare errori precedenti e di più facile comprensione, può rilevare errori che la tipizzazione anatra non può e può essere controllata staticamente (in modo insolito senso ma è ancora al di fuori del runtime). D'altra parte, la tipizzazione anatra è stata per molto tempo il modo Pythonic, non impone il sovraccarico cognitivo della tipizzazione statica, è meno dettagliata e accetterà tutti i tipi vitali e poi alcuni.


2
-1: mypy si definisce specificamente un "controllo del tipo statico", quindi non sono sicuro di dove tu abbia ottenuto "il controllo del tipo deve essere eseguito in fase di esecuzione" da.
Kevin,

@Kevin In retrospettiva, quella era una digressione non necessaria, ma per approfondire ulteriormente, i suggerimenti sul tipo di Python vengono trasformati in dati di runtime ed mypyè un modulo Python che utilizza importlibper accedere a tali dati. Se si tratti di "controllo statico del tipo" è una domanda filosofica, ma è diversa da ciò che la maggior parte si aspetterebbe dal momento che sono coinvolti il ​​normale interprete di linguaggio e le macchine di importazione.
Prassolitico,

4
Neanche questo è vero. Esso utilizza typed_ast, che di per sé è solo un clone di ast con le caratteristiche supplementari. ast non importa i moduli; li analizza in un albero di sintassi astratto.
Kevin,

18

Ecco un esempio per cui la digitazione delle anatre è cattiva senza sapere quando è pericolosa. Ad esempio: ecco il codice Python (forse omettendo il rientro corretto), nota che questa situazione è evitabile prendendoti cura delle funzioni isinstance e issubclassof per assicurarti che quando hai davvero bisogno di un'anatra, non ottieni una bomba.

class Bomb:
    def __init__(self):
        ""

    def talk(self):
        self.explode()

    def explode(self):
        print "BOOM!, The bomb explodes."

class Duck:
    def __init__(self):
        ""
    def talk(self):
        print "I am a duck, I will not blow up if you ask me to talk."    

class Kid:
    kids_duck = None

    def __init__(self):
        print "Kid comes around a corner and asks you for money so he could buy a duck."

    def takeDuck(self, duck):
        self.kids_duck = duck
        print "The kid accepts the duck, and happily skips along"

    def doYourThing(self):
        print "The kid tries to get the duck to talk"
        self.kids_duck.talk()

myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()

36
Anche con il controllo del tipo, puoi creare un class EvilDuck(Duck)discorso e sovrascriverlo (). O più probabilmente, class ChineseCancerDuck(Duck)con un brutto effetto collaterale che non si manifesta fino a anni dopo. Faresti meglio a sorvegliare tuo figlio (e testare a fondo i suoi giocattoli :)
Brett Thomas,

36
Le bombe non parlano. Non aggiungere metodi senza senso e questo non accadrà.
destra del

7
@Dmitry, questa è la critica comune di Duck Typing: en.wikipedia.org/wiki/Duck_typing#Criticism ... in pratica stai dicendo che qualsiasi interfaccia per la quale la semantica non è imposta dal linguaggio è malvagia. Credo che questo sia più l'approccio di Java. Il punto centrale della tipizzazione anatra di Python è che funziona solo quando c'è una convenzione comunemente confermata sul significato di interfacce specifiche. Ad esempio, potresti inserire un sacco di codice Python sovrascrivendo l' __file__attributo (comunemente usato per identificare oggetti simili a file) per significare qualcos'altro.
Dan Lenski,

2
Tutto questo si riduce alla vecchia battuta "Dottore, fa male quando lo faccio." ... "Allora non farlo.". Insoddisfacente per qualcuno che è abituato a "se compila, corre", ma è per questo che il test dell'ossessione è cresciuto dal mondo del linguaggio dinamico.
clacke,

1
@clacke in pratica, è troppo costoso imporre rigorosamente i tipi in fase di esecuzione perché TUTTO deve essere un oggetto (per mappare da stringa a qualsiasi tipo possibile) e troppo comodo per non avere il ducktyping perché il ducktyping consente tecniche di prototipazione davvero potenti che superano le cose che sono normalmente molto difficili da fare con le interfacce rigide. Inoltre, qualsiasi linguaggio statico deve affrontare un punto in cui deve creare la tipizzazione anatra tramite librerie dinamiche, valutazione e stringificazione o interfacce, e queste cose non lo rendono intrinsecamente malvagio, solo molto potente.
Dmitry,

12
isinstance(o, str)

Link ai documenti


1
Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia.
EKons

7

Penso che la cosa bella dell'uso di un linguaggio dinamico come Python sia che non dovresti davvero controllare qualcosa del genere.

Vorrei solo chiamare i metodi richiesti sul tuo oggetto e catturare un AttributeError. In seguito ciò ti consentirà di chiamare i tuoi metodi con altri oggetti (apparentemente non correlati) per eseguire diverse attività, come deridere un oggetto per il test.

L'ho usato molto per ottenere dati dal Web con i urllib2.urlopen()quali restituisce un file come un oggetto. A sua volta, questo può essere passato a quasi tutti i metodi che leggono da un file, perché implementa lo stesso read()metodo di un file reale.

Ma sono sicuro che ci sia un tempo e un luogo per l'uso isinstance(), altrimenti probabilmente non ci sarebbe :)


Un buon esempio di quando devi usarlo è se stai analizzando un oggetto json dinamico. Non si sa in anticipo se un campo è una stringa o un dizionario.
Gray,

6

Per convalide di tipi più complessi mi piace l'approccio di typeguard alla validazione basato su annotazioni di suggerimenti di tipo Python:

from typeguard import check_type
from typing import List

try:
    check_type('mylist', [1, 2], List[int])
except TypeError as e:
    print(e)

Puoi eseguire validazioni molto complesse in modo molto pulito e leggibile.

check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) 

6

Puoi controllare il tipo di una variabile usando __name__ di un tipo.

Ex:

>>> a = [1,2,3,4]  
>>> b = 1  
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'

Grazie, questo è il codice segreto che volevo quando lo visualizzavo come feedback per l'utente. Mi ci è voluto troppo tempo per trovarlo ...
Aaron D. Marasco,

5

A Hugo:

Probabilmente intendi listpiuttosto che array, ma questo indica l'intero problema con il controllo del tipo: non vuoi sapere se l'oggetto in questione è un elenco, vuoi sapere se è un qualche tipo di sequenza o se è un singolo oggetto. Quindi prova a usarlo come una sequenza.

Supponi di voler aggiungere l'oggetto a una sequenza esistente oppure, se si tratta di una sequenza di oggetti, aggiungili tutti

try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

Un trucco con questo è se stai lavorando con stringhe e / o sequenze di stringhe - è difficile, dato che una stringa viene spesso considerata come un singolo oggetto, ma è anche una sequenza di caratteri. Peggio ancora, in quanto è davvero una sequenza di stringhe a lunghezza singola.

Di solito scelgo di progettare la mia API in modo che accetti solo un singolo valore o una sequenza - semplifica le cose. Non è difficile mettere un [ ]valore intorno al tuo singolo valore quando lo passi, se necessario.

(Anche se questo può causare errori con le stringhe, come sembrano (sono) sequenze.)


0

Un modo semplice per controllare il tipo è di confrontarlo con qualcosa di cui conosci il tipo.

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True


-7

Puoi controllare con la riga sotto per verificare quale tipo di carattere è il valore dato:

def chr_type(chrx):
    if chrx.isalpha()==True:
        return 'alpha'
    elif chrx.isdigit()==True:
        return 'numeric'
    else:
        return 'nothing'

chr_type("12)

3
Sei sicuro di non voler eliminare questa risposta @Venkatesan?
Gray,
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.