La funzione type() è una delle basi per lavorare con i tipi in Python. Come parte dell’im­ple­men­ta­zio­ne è uno dei punti chiave del lin­guag­gio di pro­gram­ma­zio­ne.

A cosa serve la funzione type() in Python?

La funzione type() è usata in Python per due ap­pli­ca­zio­ni com­ple­ta­men­te diverse:

  1. De­ter­mi­na­re il tipo di un oggetto Python
  2. Creare di­na­mi­ca­men­te un nuovo tipo

Ana­liz­zia­mo in­nan­zi­tut­to il primo caso, che è estre­ma­men­te utile nell’uso quo­ti­dia­no.

De­ter­mi­na­re il tipo di un oggetto con type()

Python è un lin­guag­gio tipizzato di­na­mi­ca­men­te. Questo significa che i tipi vengono de­ter­mi­na­ti solo durante l’ese­cu­zio­ne e che sono legati a valori anziché a variabili. Ne consegue la necessità di de­ter­mi­na­re il tipo di un oggetto nel tempo di ese­cu­zio­ne.

Chiamiamo la funzione type() di Python e passiamo un oggetto come parametro unico. Viene re­sti­tui­to il tipo di oggetto, ad esempio int o str:

# Type of `42` is `int`
assert type(42) == int
# Type of `str(42)` is `str`
assert type(str(42)) == str
Python

Se nella REPL di Python chiamiamo la funzione type(), la rap­pre­sen­ta­zio­ne di testo contiene “class” anziché “type”:

# Returns "<class 'int'>" inside REPL
type(42)
Python

A prima vista può scon­cer­ta­re, ma ha as­so­lu­ta­men­te senso, perché in Python: “Eve­ry­thing is an object” (tutto è un oggetto). In Python il tipo di un oggetto cor­ri­spon­de alla sua classe. Pertanto, chiamare la funzione type() ge­ne­ral­men­te equivale a leggere l’attributo __class__:

# Should hold in most cases
assert type(obj) is obj.__class__
Python

Creare un nuovo tipo con type()

Ana­liz­zia­mo ora la seconda pos­si­bi­li­tà di impiego della funzione type(). Chiamata con tre argomenti, la funzione ci permette di generare di­na­mi­ca­men­te un nuovo tipo:

type(name, bases, dict, **kwds)
Python

In questa forma, la funzione type() in Python funge ana­lo­ga­men­te alla parola chiave class. Il codice Type = type("Type", bases, dict) cor­ri­spon­de a grandi linee alla de­fi­ni­zio­ne di classe seguente:

class <Type>(<bases>):
    <dict>
Python

Più avanti ti mo­stre­re­mo alcuni esempi pratici di utilizzo della funzione type() in Python per creare nuovi tipi. Ma prima vediamo un prospetto degli argomenti:

name bases dict **kwds
Nome del nuovo tipo come stringa Tupla con classi base Dict con attributi della nuova classe Altri argomenti per l’istan­zia­zio­ne di me­ta­clas­si
Consiglio

Con Deploy Now di IONOS di­stri­bui­sci fa­cil­men­te siti web e app tramite GitHub.

Come agisce la funzione type() in Python?

Quando si utilizza la funzione type() per de­ter­mi­na­re il tipo di un oggetto, il valore re­sti­tui­to non è una stringa, ma un oggetto autonomo:

# Value returned by `type(42)` is not a string
assert type(42) != 'int'
# We get back an object named `int`
assert type(42) == int
Python

Ana­liz­zia­mo alcuni esempi di valori re­sti­tui­ti dalla funzione type() per oggetti di tipo com­ple­ta­men­te diverso:

# Python objects of different types
different_objs = None, True, 42, 'John', ('Walter', 'White'), ...
# Print out the type of each object
for obj in different_objs:
    print(f"{obj}: {type(obj)}")
Python
Chiamata type() Rap­pre­sen­ta­zio­ne testuale
type(None) <class 'NoneType'>
type(True) <class 'bool'>
type(42) <class 'int'>
type('John') <class 'str'>
type(('Walter', 'White')) <class 'tuple'>
type(...) <class 'ellipsis'>

Si pone la domanda: qual è il tipo dell’oggetto re­sti­tui­to da type()? Proviamo. Chiamiamo la funzione type() di Python e passiamo il valore re­sti­tui­to di un’altra chiamata type():

# Returns: "<class 'type'>"
type(type(42))
Python

Notiamo che oltre alla funzione type() integrata di Python esiste l’omonimo tipo type. Questo è il tipo di tutti gli altri tipi Python, come si vede nell’esempio:

# DifferentPython objects
different_objs = None, True, 42, 'John', ('Walter', 'White'), ...
# Check the type of each object's type
for obj in different_objs:
    # Show that the type's type is always `type`
    assert type(type(obj)) is type
Python

Il tipo di ciascun tipo Python è dunque type. Suona strano? E non è ancora finita! Anche il tipo dell’oggetto type è a sua volta type. E continua all’infinito, come un gatto che si morde la coda:

# It's `type` all the way down
assert type(type(type(type))) is type
Python

Per sbro­glia­re la matassa occorre acquisire una co­no­scen­za più ap­pro­fon­di­ta del sistema OOP di Python. L’oggetto type integrato in Python rap­pre­sen­ta una co­sid­det­ta me­ta­clas­se. Una me­ta­clas­se si comporta con una classe come una classe con un oggetto. In altre parole, una me­ta­clas­se è un modello (in inglese: template) di una classe, mentre una classe è un modello di un oggetto:

Template Istanza
Classe Oggetto
Me­ta­clas­se Classe
Esempio: type int, str, ecc.
Esempio:int 42
Esempio: str “Walter White”

Come si usa la funzione type() in Python?

So­li­ta­men­te per de­ter­mi­na­re il tipo di un oggetto nel tempo di ese­cu­zio­ne viene uti­liz­za­ta la funzione type() di Python. È utile perché Python è un lin­guag­gio tipizzato di­na­mi­ca­men­te. In un lin­guag­gio tipizzato sta­ti­ca­men­te come Java viene collegato a una variabile un tipo per di­chia­ra­zio­ne, che non è mo­di­fi­ca­bi­le nel tempo di ese­cu­zio­ne:

// Declare variable as `boolean`
boolean answer;
// Attempting to assign `int` value
// Throws type error
answer = 42;
Java

Le variabili in Python sono solo nomi che rimandano a valori tipizzati. Durante l’ese­cu­zio­ne del codice è possibile far riferire un nome a un valore con un altro tipo in qualsiasi momento. Per de­ter­mi­na­re il tipo di una variabile Python nel tempo di ese­cu­zio­ne abbiamo quindi bisogno della funzione type():

# Assign boolean value
answer = True
# Show that type is `bool`
assert type(answer) is bool
# Reassign integer value
answer = 42
# Show that type is now `int`
assert type(answer) is int
Python

Verifica del tipo degli argomenti della funzione in Python

Per definire una funzione è ne­ces­sa­rio con­trol­la­re che gli argomenti ri­spet­ti­no de­ter­mi­na­ti criteri. Ad esempio, un argomento può essere compreso solo entro de­ter­mi­na­ti limiti, oppure sono ammessi solo argomenti di tipo idoneo. In questo modo si evitano errori durante il tempo di ese­cu­zio­ne.

Vediamo l’uso della funzione type() con un esempio: definiamo una funzione che aggiunge una lista di numeri. Affinché funzioni dobbiamo as­si­cu­rar­ci che ciascun argomento sia ef­fet­ti­va­men­te un numero. Uti­liz­zia­mo type() all’interno di un’istru­zio­ne assert:

# Function to add up numeric arguments
def add_numbers(*args):
    result = 0
    # Check each argument
    for arg in args:
        # Abort with error message if argument is not an `int` or `float`
        assert type(arg) in (int, float), f"Argument `{arg}` is not a number"
        # Add argument's value to total
        result += arg
    return result
# Show that it works for numbers
assert add_numbers(35, 7) == 42
# The following will fail
add_numbers(29, 'thirteen')
Python

Debug nella REPL di Python con la funzione type()

Uno dei vantaggi di usare un lin­guag­gio in­ter­pre­ta­to come Python è l’ese­cu­zio­ne in­te­rat­ti­va del codice in REPL (Read-Eval-Print Loop). Questo approccio consente una pro­to­ti­pa­zio­ne rapida e un debug diretto tramite l’ispezione degli oggetti presenti in memoria.

Im­ma­gi­nia­mo lo scenario seguente: il nostro codice ha una variabile answer che deve contenere un valore booleano. Rileviamo che il tipo non cor­ri­spon­de alle nostre aspet­ta­ti­ve e uti­liz­zia­mo la funzione type() di Python per re­sti­tui­re il tipo effettivo. Come si vede, abbiamo inav­ver­ti­ta­men­te scritto il valore booleano tra vir­go­let­te, un errore di di­stra­zio­ne che so­prat­tut­to i prin­ci­pian­ti compiono di frequente:

# Accidentally set to string
answer = 'False'
# Assertion will fail
assert type(answer) is bool
# Correct to boolean value
answer = False
# Now assertion holds
assert type(answer) is bool
Python

Creazione dinamica di classi Python con la funzione type()

Come abbiamo visto, le classi Python possono essere generate di­na­mi­ca­men­te (ovvero durante il tempo di ese­cu­zio­ne) con la funzione type(). Questo è utile, tra l’altro, per le famiglie di classi. Vediamolo con un esempio di tag HTML. Per prima cosa generiamo una classe base Tag, i cui oggetti possono essere rap­pre­sen­ta­ti come codice HTML:

# Class representing HTML tag
class Tag:
    # Initialize HTML tag with contents
    def __init__(self, *args):
        # Join contents of tag
        self.content = "".join([arg.__str__() for arg in args])
    # String representation returns HTML
    def __str__(self):
        return f"<{self.name}>{self.content}</{self.name}>"
Python

Dopodiché, con l’ere­di­ta­rie­tà spe­cia­liz­zia­mo la classe base nei ri­spet­ti­vi tag specifici come <p> o <h1>. A questo scopo chiamiamo la funzione type() con tre argomenti:

# Create `P` class
P = type('P', (Tag,), {"name": 'p'})
Python
  1. Nome della nuova classe come stringa.

  2. Tupla con classi base.

    Python consente l’ere­di­ta­rie­tà multipla; per derivare una sola classe usiamo la notazione (ClassName,).

  3. Dict con il nome della classe ed even­tual­men­te ulteriori voci.

    Le voci possono anche essere funzioni.

In seguito istan­zia­mo un tag p e ve­ri­fi­chia­mo che la rap­pre­sen­ta­zio­ne funzioni cor­ret­ta­men­te:

# Instantiate `p` tag
greeting = P("Hello world")
assert str(greeting) == '&lt;p&gt;Hello world&lt;/p&gt;'
Python

Si ottiene lo stesso effetto con la de­fi­ni­zio­ne di classe analogica:

# Create `P` class
class P(Tag):
    name = 'p'
Python

Un altro esempio: creiamo classi per i titoli con type(). Poiché la ge­ne­ra­zio­ne delle classi è dinamica, con una com­pren­sio­ne di lista è possibile creare le classi per tutti e sei i livelli di titolo in un colpo solo:

h_1_to_6 = ( f"h{n}" for n in range(1, 7) )
headings = [type(heading, (Tag,), {"name": heading}) for heading in h_1_to_6]
Python

Come abbiamo mostrato, vale la pena di usare la funzione type() per generare co­mo­da­men­te più sot­to­clas­si correlate. Vediamo un esempio più complesso in cui definiamo delle classi per rap­pre­sen­ta­re carte da gioco. In­nan­zi­tut­to, definiamo una su­per­clas­se Card con la parola chiave class:

# Class representing abstract playing card
class Card:
    def __init__(self, number):
        self.number = number
    # String representation
    def __str__(self):
        return f"{self.number} of {self.suite}"
Python

In seguito generiamo sot­to­clas­si per i quattro semi delle carte mediante type():

# Create concrete types for each suite
Clubs = type('Clubs', (Card,), {'suite': 'Clubs'})
Diamonds = type('Diamonds', (Card,), {'suite': 'Diamonds'})
Hearts = type('Hearts', (Card,), {'suite': 'Hearts'})
Spades = type('Spades', (Card,), {'suite': 'Spades'})
Python

Ora le singole carte possono essere istan­zia­te fa­cil­men­te:

# Instantiate a 7 of Spades
card = Spades(7)
# Show that it worked
assert str(card) == '7 of Spades'
Python

Quali sono i limiti della funzione type()?

La funzione type() di Python è utile, anche se in alcuni casi presenta li­mi­ta­zio­ni. For­tu­na­ta­men­te Python offre soluzioni idonee; vediamone alcune.

Scom­po­si­zio­ne di gerarchie di ere­di­ta­rie­tà con isin­stan­ce()

type() determina solo il tipo effettivo di un oggetto Python, senza però prendere in con­si­de­ra­zio­ne la gerarchia di ere­di­ta­rie­tà. Ne consegue un dilemma, che il­lu­stria­mo con il nostro esempio delle carte di gioco dell’ultimo paragrafo. Il tipo di un 7 di picche dovrebbe essere sia “picche” sia “carta da gioco”. Non è tuttavia possibile de­ter­mi­nar­lo con type():

# Create a Seven of Spades
card = Spades(7)
# Our card is a Spade alright
assert type(card) is Spades
# But not a card??
assert type(card) is not Card
Python

Per scomporre cor­ret­ta­men­te il po­li­mor­fi­smo alla base ri­cor­ria­mo alla funzione isinstance().

# Seven of Spades is a `Spade`
assert isinstance(card, Spades)
# And is also a `Card`
assert isinstance(card, Card)
Python

Sem­pli­fi­ca­re il ri­co­no­sci­men­to del tipo di oggetto Python con match-case

Come abbiamo mostrato sopra, spesso la funzione type() è impiegata per de­ter­mi­na­re il tipo di un oggetto durante il tempo di ese­cu­zio­ne. Per di­stin­gue­re più tipi diversi fra loro si utilizza even­tual­men­te un costrutto if-elif-else:

# Determine type of object
if type(obj) is int:
    print("Int")
elif type(obj) is float:
    print("Float")
elif type(obj) is ...:
    print("...")
else:
    print("Something else")
Python

Dalla versione 3.10 Python conosce tuttavia l’istru­zio­ne match-case, che permette tra l’altro di ri­co­no­sce­re i tipi senza chiamare la funzione type().

All’interno di un blocco case si possono usare funzioni di co­strut­to­re come int(obj) o str(obj). Il blocco trova la cor­ri­spon­den­za quando l’oggetto ha il tipo relativo:

# Example object
obj = 42
# Determine object type
match obj:
    case int(obj):
        print(f"{obj} is `int`")
    case float(obj):
        print(f"{obj} is `float`")
    case _:
        print(f"{obj} is something else")
Python
Consiglio

Per fa­mi­lia­riz­za­re con il lin­guag­gio, utilizza anche il nostro tutorial su Python e il nostro prospetto degli operatori Python.

Vai al menu prin­ci­pa­le