# 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`:

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

Se nella REPL di Python chiamiamo la funzione type(), **la rappresentazione di testo contiene “class” anziché “type”**:

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

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__`:

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

### 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:

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

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:

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

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:

<table>
  <thead>
    <tr>
      <th>`name`</th>
      <th>`bases`</th>
      <th>`dict`</th>
      <th>`**kwds`</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Nome del nuovo tipo come stringa</td>
      <td>Tupla con classi base</td>
      <td>Dict con attributi della nuova classe</td>
      <td>Altri argomenti per l’istanziazione di metaclassi</td>
    </tr>
  </tbody>
</table>

Consiglio Con [Deploy Now](https://www.ionos.it/hosting/deploy-now "Deploy Now di IONOS") 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**:

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

Analizziamo alcuni **esempi di valori restituiti dalla funzione type()** per oggetti di tipo completamente diverso:

```Python
# 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)}")
```

<table>
  <thead>
    <tr>
      <th>Chiamata type()</th>
      <th>Rappresentazione testuale</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>`type(None)`</td>
      <td>`<class 'NoneType'>`</td>
    </tr>
    <tr>
      <td>`type(True)`</td>
      <td>`<class 'bool'>`</td>
    </tr>
    <tr>
      <td>`type(42)`</td>
      <td>`<class 'int'>`</td>
    </tr>
    <tr>
      <td>`type('John')`</td>
      <td>`<class 'str'>`</td>
    </tr>
    <tr>
      <td>`type(('Walter', 'White'))`</td>
      <td>`<class 'tuple'>`</td>
    </tr>
    <tr>
      <td>`type(...)`</td>
      <td>`<class 'ellipsis'>`</td>
    </tr>
  </tbody>
</table>

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():

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

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:

```Python
# 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
```

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:

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

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:

<table>
  <thead>
    <tr>
      <th>Template</th>
      <th>Istanza</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Classe</td>
      <td>Oggetto</td>
    </tr>
    <tr>
      <td>Metaclasse</td>
      <td>Classe</td>
    </tr>
    <tr>
      <td>Esempio: `type`</td>
      <td>`int`, `str`, ecc.</td>
    </tr>
    <tr>
      <td>Esempio:`int`</td>
      <td>42</td>
    </tr>
    <tr>
      <td>Esempio: `str`</td>
      <td>“Walter White”</td>
    </tr>
  </tbody>
</table>

## 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:

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

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():

```Python
# 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
```

### 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`:

```Python
# 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')
```

### 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:

```Python
# 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
```

### 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:

```Python
# 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}>"
```

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:

```Python
# Create `P` class
P = type('P', (Tag,), {"name": 'p'})
```

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:

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

Si ottiene lo stesso effetto con la **definizione di classe analogica**:

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

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:

```Python
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]
```

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`:

```Python
# 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}"
```

In seguito **generiamo sottoclassi per i quattro semi delle carte** mediante type():

```Python
# 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'})
```

Ora **le singole carte possono essere istanziate facilmente**:

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

## 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():

```Python
# 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
```

Per **scomporre correttamente il polimorfismo alla base** ricorriamo alla funzione `isinstance()`.

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

### 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`:

```Python
# 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")
```

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:

```Python
# 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")
```

Consiglio Per familiarizzare con il linguaggio, utilizza anche il nostro [tutorial su Python](https://www.ionos.it/digitalguide/siti-web/programmazione-del-sito-web/tutorial-python/) e il nostro prospetto degli [operatori Python](https://www.ionos.it/digitalguide/siti-web/programmazione-del-sito-web/operatori-python/).


This is a markdown version of: [https://www.ionos.it/digitalguide/siti-web/programmazione-del-sito-web/la-funzione-type-in-python/](https://www.ionos.it/digitalguide/siti-web/programmazione-del-sito-web/la-funzione-type-in-python/) for AI/LLM consumption.