lettura simple

La classe type in Python

In Python, tutto è un oggetto. Questo include anche le classi. Le classi stesse sono create da un'altra classe chiamata `type`.

La classe `type` è la metaclasse predefinita in Python, dove una metaclasse è semplicemente una classe delle classi: una classe che crea altre classi e ne definisce il comportamento.

In altre parole, le classi generano oggetti (istanze) ma le classi stesse sono delle istanze ovvero degli oggetti.

Ad esempio, crea una classe personalizzata:

class Foo:
    pass

Questa classe `Foo` è in realtà un'istanza di `type`.

Puoi verificarlo verificarlo facilmente.

print(type(foo))

<class 'type'>

Questo conferma che la classe `Foo` è stata creata dalla metaclasse `type`.

In generale, tutte le classi di Python sono istanze della classe `type`.

Quindi, anche le classi predefinite di Python lo sono. Ad esempio, la classe `list` è anch'essa un'istanza di `type`

print(isinstance(list, type))

True

Lo stesso vale per tutte le altre classi predefinite di Python, come int(), float(), str(), ecc.

Un fatto interessante è anche la classe `type` è un'istanza di se stessa

print(isinstance(type, type))

True

E' una cosa apparentemente "strana", che non può accadere agli insiemi, perché nessun insieme può contenere tutti gli insiemi perché dovrebbe contenere anche se stesso, ma è del tutto accettabile quando si parla di classi.

Creazione dinamica delle dlassi

La classe `type` può essere utilizzata non solo per ottenere il tipo di un oggetto, ma anche per creare nuove classi in modo dinamico.

Quando chiami type() con un solo argomento, ottieni il tipo di oggetto:

myList = [1, 2,3]
print(type(myList))

<class 'list'>

Se invece chiami  `type` con tre argomenti, crei una nuova classe.

I tre argomenti sono il nome della nuova classe, una tupla delle classi base da cui eredita e un dizionario degli attributi della classe. 

Ad esempio, definisci una semplice classe:

class Foo:
    pass

internamente, Python fa qualcosa di simile a:

Foo = type('Foo', (), {})

In questo caso `type` prende tre argomenti:

  1. Il nome della classe: `'Foo'`
  2. Una tupla di classi base: `()` in questo caso è vuota perché non c'è ereditarietà.
  3. Un dizionario di attributi: `{}` in questo caso è vuoto, poiché non abbiamo definito attributi o metodi in questa classe.

Questo significa che la definizione di una classe non è altro che un’istanza di `type` che segue queste specifiche.

Esempio 2

Vediamo un esempio più complesso:

Crea una nuova classe usando `type`

Foo = type('Foo', (), {'year': 2020})

In questo caso:

  • 'Foo'` è il nome della nuova classe.
  • `()` indica che `Foo` non eredita da altre classi.
  • `{'year': 2020}` è un dizionario che contiene un attributo chiamato `year` con valore `2020`.

Ora puoi usare questa classe come qualsiasi altra.

Ad esempio, puoi creare un'istanza

obj = Foo()

Una volta creato un oggetto, puoi accedere ai suoi attributi.

print(obj.year)

2020

Esempio 3 (attributi e metodi)

Grazie alla classe type puoi anche creare dinamicamente delle classi con attributi e metodi.

Puoi aggiungere metodi a una classe passando le funzioni come valori nel dizionario.

Ad esempio, definisci una funzione chiamata 'MiaFunzione' che userai come metodo '__init__' della nuova classe

def MiaFunzione(self, year):
    self.year = year

La funzione ha un attributo 'year'.

Ora crea una nuova classe 'Foo' associando la funzione 'MiaFunzione' al metodo '__init__'.

Foo = type('Foo', (), {'__init__': MiaFunzione})

In questo caso, hai passato al dizionario il metodo `__init__` che imposta un attributo `year` sull'istanza.

Ora puoi creare un'istanza della nuova classe.

obj = Foo(2020)

Infine, puoi accedere agli attributi dell'oggetto.

print(obj.year) 

2020

Esempio 4 (ereditarietà)

Ad esempio, crea queste classi

class myClass1:
    pass

class myClass2:
    pass

Poi crea una nuova classe 'myClass3' che eredita da 'myClass1' e 'myClass2' e ha un attributo `language` con valore 'python'.

Il secondo argomento di `type` stabilisce la gerarchia di ereditarietà. 

myClass3 = type('myClass3', (myClass1, myClass2), {'language': 'python'})

Il risultato finale è come se tu avessi definito una nuova classe.

A questo punto puoi creare nuove istanze dalla classe 'myClass3'.

oggetto = myClass3()
print(isinstance(oggetto, myClass3))

true

Quando crei una classe con più classi base, Python determina l'ordine in cui cercare gli attributi e i metodi attraverso l’ordine dato nella tupla di classi.

Questo ordine è definito nell'attributo `__mro__` (Method Resolution Order) della classe.

print(myClass3.__mro__)

(<class '__main__.myClass3'>, <class '__main__.myClass1'>, <class '__main__.myClass2'>, <class 'object'>)

Spero che questo chiarisca il concetto! Se hai domande, non esitare a chiedere.




Se qualcosa non ti è chiaro, scrivi la tua domanda nei commenti.




FacebookTwitterLinkedinLinkedin