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:
- Determinare il tipo di un oggetto Python
- 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
PythonSe nella REPL di Python chiamiamo la funzione type(), la rappresentazione di testo contiene “class” anziché “type”:
# Returns "<class 'int'>" inside REPL
type(42)
PythonA 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__
PythonCreare 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)
PythonIn 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>
PythonPiù 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 |
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
PythonAnalizziamo 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)}")
PythonChiamata 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))
PythonNotiamo 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
PythonIl 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
PythonPer 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;
JavaLe 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
PythonVerifica 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')
PythonDebug 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
PythonCreazione 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}>"
PythonDopodiché, 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-
Nome della nuova classe come stringa.
-
Tupla con classi base.
Python consente l’ereditarietà multipla; per derivare una sola classe usiamo la notazione
(ClassName,)
. -
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) == '<p>Hello world</p>'
PythonSi ottiene lo stesso effetto con la definizione di classe analogica:
# Create `P` class
class P(Tag):
name = 'p'
PythonUn 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]
PythonCome 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}"
PythonIn 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'})
PythonOra 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'
PythonQuali 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
PythonPer 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)
PythonSemplificare 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")
PythonDalla 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")
PythonPer familiarizzare con il linguaggio, utilizza anche il nostro tutorial su Python e il nostro prospetto degli operatori Python.