Python

Tipi: numeri interi              

Python dispone di un potente type system nativo estremamente ampio ed efficiente. E' talmente completo che realmente vengono coperte gran parte delle necessità dei programmatori. In particolare l'uso degli strumenti che il linguaggio ci mette a disposizione rende la programmazione molto più semplice, sicura ed efficente ed aumenta la possibilità di una standardizzazione del codice e conseguentemente la sua manutenibilità e leggibilità, rispetto a quanto si può giocoforza ottenere con l'uso di strumenti fortemente customizzati. Pytohn presenta il seguente elenco di tipi base (che chiameremo col loro nome inglese, dal momento che è quello che più spesso troverete nella letteratura dedicata):

  • Numeri
  • Stringhe
  • Dizionari
  • Liste
  • Tuple
  • Files
  • Sets
  • Altri (booleani, funzioni, moduli, classi, None ecc...)

Questi vi condurranno nella grande maggioranza dei casi alla soluzione del vostro problema. Dal punto di vista della tipizzazione Python è un linguaggio a tipizzazione dinamica il che vuol dire che non è il compilatore che si occupa dell'attribuzione del tipo ma il tutto avviene a runtime. Questo ci rende possibile scrivere sequenze tipo:

>>> a = 5
>>> print(a)
5
>>> a = 'aa'
>>> print(a)
aa

cosa che in un linguaggio a tipizzazione statica (C, Java ecc..) non è ammesso. La tipizzazione è anche di tipo forte il che garantisce un utilizzo coerente delle variabili di quel tipo, ovvero non sono permesse operazioni non congrue con quel tipo... ad esempio non possiamo cercare di calcolare la radice quadrata di una stringa, per fare un esempio grossolano. Una volta attribuito un tipo dovete lavorare in una maniera che segua le regole imposte dal linguaggio per quel tipo.

Vediamo quindi in dettaglio qualche cosa relativo ai tipi ed iniziamo da quelli (apparentemente) più semplici ovvero

i numeri

All'origine di tutto abbiamo un classe astratta (vedremo di che si tratta più avanti, naturalmente) che si chiama, semplicemente numbers. Per verificare se un valore qualunque appartiene a questa classe, tramite le sue sottoclassi, esiste il metodo isinstance(x, Number). Da essa discendono direttamente 4 chiamiamoli sottotitpi:

Complessi
Reali
Razionali
Integrali

Sono le 4 classi base da cui discendono tutti gli altri tipi numerici. Questo se guardiamo agli "internals". Da un punto di vista pratico tuttavia è utile considerare una suddivisione un po' più raffinata e in linea con quella che si trova in quasi tutti i manuali; pertanto considereremo i seguenti tipi di numero:

interi - privi di parte frazionale
floating point - che hanno una parte frazionale
complessi - che contengono una parte immaginaria
decimali - a precisione fissa
razionali - caratterizzati da un numeratore ed un denominatore.

In Python 3 gli interi, a differenza di quanto avveniva in Python 2.x, costituiscono un tipo monolitico di precisione limitata solamente dalle risorse hardware a disposizione. Non sono quindi previsti suffissi come l o L che erano necessari  nella vecchia versione di Python. I numeri interi supportano le consuete operazioni con qualche piccola particolarità, vediamo il tutto con un semplice esempio:

  Esempio 2.1
1
2
3
4
5
6
7
8
9
x = 7
y = 3
print(x + y)
print(x - y)
print(x * y)
print(x / y)
print(x % y)
print(x // y)
print(x ** y)

riga 3: la classica somma
riga 4: sottrazione
riga 5: moltiplicazione
riga 6: la divisione. Questa restituisce il risultato "vero", quello con la virgola. Infatti 7/3 fa 2.3333333333333335 (Vedremo perchè c'è quel 5 finale)
riga 7: il resto della divisione
riga 8: restituisce il quoziente, quindi la parte intera della divisione.
riga 9: è l'elevamento a potenza

Come si vede nulla di speciale ma un paio di operatori, parlo in particolare di quelli alle righe 8 e 9, piuttosto comodi e immediati laddove altri linguaggi costringono a qualche piccola acrobazia. Oltre ai simboli appena introdotti potranno esservi di utilità immediata i seguenti:

  Esempio 2.2
1
2
3
4
5
6
7
x = 7
y = -3
print(abs(y))
print(divmod(7, 3))
print(pow(x, y))
print(pow(10, 2, 5))
print(round(2.14567, 3))

che presenta il seguente output:

3
(2, 1)
0.0029154518950437317
0
2,146

riga 3: valore assoluto
riga 4: restituisce una "tuple", che vedremo più avanti cos'è, costituita dal risultato della divisione e dal suo resto
riga 5: è l'elevamento a potenza, in pratica un alias per **. Qui abbiamo 7 elevato - 3, ovvero 1/7^3.
riga 6: sta per (10 ** 2) % 5.  Ovvero pow(x, y, z) sta per (x^y)%z
riga 7: round(x, y) esegue un arrotondamento di x definito su y cifre decimali.

Tutti questi sono operatori per così dire rapidi che vi verranno molto spesso in aiuto.

E' da sottolineare ancora che il range dei numeri interi è realmente ampio, tanto da permettervi senza problemi calcoli una volta davvero impensabili (ovviamente grazie anche alla potenza delle piattaforme odierne... si tratta di una sinergia completa, sotto questo aspetto):

>>> 3 ** 1000
13220708194808066368904552597521443659654220327521481676649203682268285973467048
99540778313850608061963909777696872582355950954582100618911865342725257953674027
62022519832080387801477422896484127439040011758861804112894781562309443806156617
30540866744905061781254803444055470543970388958174653682549161362208302685637785
82290228416398307887896918556404084898937609373242171846359938695516765018940588
109060426089671438864102814350385648747165832010614366132173102768902855220001

Python naturalmente prevede anche le classiche rappresentazioni binaria, ottale ed esadecimale. Come in altri linguaggi queste tipologie sono introdotte da specifici prefissi:

0x - 0X per gli esadecimali
0b - 0B per i binari
0o - 0O per gli ottali

>>> print(0x4e)
78
>>> print(0o77)
63
>>> print(0b11)
3
>>> print(0b12)
File "<stdin>", line 1
print(0b12)
^
SyntaxError: invalid syntax

L'ultimo esempio dà errore perchè il numero 2 non è accettabile in una sequenza binaria.
Esistono anche funzioni inverse che ci restituiscono, in formato stringa, conversioni inverse:

bin(x) converte l'intero x in binario
oct(x) converte l'intero x in ottale
hex(x) converte l'intero x in esadecimale

>>> bin(88)
'0b1011000'
>>> hex(88)
'0x58'
>>> oct(88)
'0o130'

Molto interessante, per puri scopi pratici, è il metodo int(). Esso trasforma un numero o una stringa in un intero, eventualmente specificando la base nel formato int(x, base):

>>> int("890")
890
>>> int("0xab",16)
171
>>> int("0o77", 8)
63

Ovviamente, anche in questo caso, i caratteri contenuti nella stringa da convertire devono essere coerenti con il formato numerico prescelto.

Altri operatori che possiamo usare con gli interi sono quelli di assegnazione:

x += 1   sta per x = x + 1 (incrementa x di una unità)
x -= 1   sta per x = x -1 (decrementa x di una unità)
x *= 3   sta per x = x * 3 (moltiplica x per 3) 
x /= 6   sta per x = x / 6 (divide per 6)
x //= 6  sta per x = x // 6 (restiuisce il quoziente di x diviso per 6)
x **= 2  sta per x ** 2 (elevamento a potenza, questo, come è chiaro, è un po' meno utile)

Gli operatori seguono le normali regole di precedenza; ad esempio:

3 + 5 * 2
fa 13 e non 16. Per ottenere quest'ultimo risultato dovremmo usare le parentesi:
(3 + 5) * 2

Una FAQ di quasi tutti i linguaggi è relativa al passaggio da intero a carattere e viceversa. Anche senza ricorrere ad un motore di ricerca la souzione è qui di seguito, facilmente ottenuta grazie ai metodi chr e ord:

>>> ord('b')
98
>>> chr(98)
'b'

Infine dobbiamo considerare, e questo vale per ogni tipologia di numero naturalmente, non solo per gli interi, che Python fornisce a corredo la potente libreria math, la quale contiene numerose funzioni pronte all'uso che coprono le necessità più comuni in campo numerico; la completa ed esaustiva documentazione di questa libreria la potete trovare sul sito ufficiale.