Python: la funzione type()

La funzione type() è una delle basi per lavorare con i tipi in Python. Come parte dell’implementazione è uno dei punti chiave del linguaggio di programmazione.

A cosa serve la funzione type() in Python?

La funzione type() è usata in Python per due applicazioni completamente diverse:

  1. Determinare il tipo di un oggetto Python
  2. Creare dinamicamente un nuovo tipo

Analizziamo innanzitutto il primo caso, che è estremamente utile nell’uso quotidiano.

Determinare il tipo di un oggetto con type()

Python è un linguaggio tipizzato dinamicamente. Questo significa che i tipi vengono determinati solo durante l’esecuzione e che sono legati a valori anziché a variabili. Ne consegue la necessità di determinare il tipo di un oggetto nel tempo di esecuzione.

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

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 rappresentazione di testo contiene “class” anziché “type”:

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

A prima vista può sconcertare, ma ha assolutamente senso, perché in Python: “Everything is an object” (tutto è un oggetto). In Python il tipo di un oggetto corrisponde alla sua classe. Pertanto, chiamare la funzione type() generalmente equivale a leggere l’attributo __class__:

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

Creare un nuovo tipo con type()

Analizziamo ora la seconda possibilità di impiego della funzione type(). Chiamata con tre argomenti, la funzione ci permette di generare dinamicamente un nuovo tipo:

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

In questa forma, la funzione type() in Python funge analogamente alla parola chiave class. Il codice Type = type("Type", bases, dict) corrisponde a grandi linee alla definizione di classe seguente:

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

Più avanti ti mostreremo 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’istanziazione di metaclassi
Consiglio

Con Deploy Now di IONOS distribuisci facilmente siti web e app tramite GitHub.

Come agisce la funzione type() in Python?

Quando si utilizza la funzione type() per determinare il tipo di un oggetto, il valore restituito 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

Analizziamo alcuni esempi di valori restituiti dalla funzione type() per oggetti di tipo completamente 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() Rappresentazione 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 restituito da type()? Proviamo. Chiamiamo la funzione type() di Python e passiamo il valore restituito 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 sbrogliare la matassa occorre acquisire una conoscenza più approfondita del sistema OOP di Python. L’oggetto type integrato in Python rappresenta una cosiddetta metaclasse. Una metaclasse si comporta con una classe come una classe con un oggetto. In altre parole, una metaclasse è un modello (in inglese: template) di una classe, mentre una classe è un modello di un oggetto:

Template Istanza
Classe Oggetto
Metaclasse Classe
Esempio: type int, str, ecc.
Esempio:int 42
Esempio: str “Walter White”

Come si usa la funzione type() in Python?

Solitamente per determinare il tipo di un oggetto nel tempo di esecuzione viene utilizzata la funzione type() di Python. È utile perché Python è un linguaggio tipizzato dinamicamente. In un linguaggio tipizzato staticamente come Java viene collegato a una variabile un tipo per dichiarazione, che non è modificabile nel tempo di esecuzione:

// 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’esecuzione del codice è possibile far riferire un nome a un valore con un altro tipo in qualsiasi momento. Per determinare il tipo di una variabile Python nel tempo di esecuzione 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 è necessario controllare che gli argomenti rispettino determinati criteri. Ad esempio, un argomento può essere compreso solo entro determinati limiti, oppure sono ammessi solo argomenti di tipo idoneo. In questo modo si evitano errori durante il tempo di esecuzione.

Vediamo l’uso della funzione type() con un esempio: definiamo una funzione che aggiunge una lista di numeri. Affinché funzioni dobbiamo assicurarci che ciascun argomento sia effettivamente un numero. Utilizziamo type() all’interno di un’istruzione 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 linguaggio interpretato come Python è l’esecuzione interattiva del codice in REPL (Read-Eval-Print Loop). Questo approccio consente una prototipazione rapida e un debug diretto tramite l’ispezione degli oggetti presenti in memoria.

Immaginiamo lo scenario seguente: il nostro codice ha una variabile answer che deve contenere un valore booleano. Rileviamo che il tipo non corrisponde alle nostre aspettative e utilizziamo la funzione type() di Python per restituire il tipo effettivo. Come si vede, abbiamo inavvertitamente scritto il valore booleano tra virgolette, un errore di distrazione che soprattutto i principianti 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 dinamicamente (ovvero durante il tempo di esecuzione) 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 rappresentati 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’ereditarietà specializziamo la classe base nei rispettivi 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’ereditarietà multipla; per derivare una sola classe usiamo la notazione (ClassName,).

  3. Dict con il nome della classe ed eventualmente ulteriori voci.

    Le voci possono anche essere funzioni.

In seguito istanziamo un tag p e verifichiamo che la rappresentazione funzioni correttamente:

# 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 definizione di classe analogica:

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

Un altro esempio: creiamo classi per i titoli con type(). Poiché la generazione delle classi è dinamica, con una comprensione 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 comodamente più sottoclassi correlate. Vediamo un esempio più complesso in cui definiamo delle classi per rappresentare carte da gioco. Innanzitutto, definiamo una superclasse 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 sottoclassi 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 istanziate facilmente:

# 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 limitazioni. Fortunatamente Python offre soluzioni idonee; vediamone alcune.

Scomposizione di gerarchie di ereditarietà con isinstance()

type() determina solo il tipo effettivo di un oggetto Python, senza però prendere in considerazione la gerarchia di ereditarietà. Ne consegue un dilemma, che illustriamo 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 determinarlo 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 correttamente il polimorfismo alla base ricorriamo alla funzione isinstance().

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

Semplificare il riconoscimento del tipo di oggetto Python con match-case

Come abbiamo mostrato sopra, spesso la funzione type() è impiegata per determinare il tipo di un oggetto durante il tempo di esecuzione. Per distinguere più tipi diversi fra loro si utilizza eventualmente 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’istruzione match-case, che permette tra l’altro di riconoscere i tipi senza chiamare la funzione type().

All’interno di un blocco case si possono usare funzioni di costruttore come int(obj) o str(obj). Il blocco trova la corrispondenza 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 familiarizzare con il linguaggio, utilizza anche il nostro tutorial su Python e il nostro prospetto degli operatori Python.

Hai trovato questo articolo utile?
Per offrirti una migliore esperienza di navigazione online questo sito web usa dei cookie, propri e di terze parti. Continuando a navigare sul sito acconsenti all’utilizzo dei cookie. Scopri di più sull’uso dei cookie e sulla possibilità di modificarne le impostazioni o negare il consenso.
Page top