12 La sintassi
Per iniziare ad imparare Python come linguaggio, partiamo dalla sua stuttura lessicale, cioè dall’insieme delle sue regole sintattiche più significative, sia per imparare le regole di composizione di programmi comprensibili all’interprete e sfrutturne appieno tutte le potenzialità, sia per essere in grado di utilizzare il codice scritto da altri.
Ogni programma Python è costituito da una serie di file di testo contenenti il codice sorgente con una certa codifica, il default essendo l’UTF-8, ed ogni file si può vedere come una sequenza di istruzioni, righe e token. Le istruzioni danno la granularità dell’algoritmo, le righe definiscono come queste istruzioni sono distribuite nel testo e, infine, i token sono gli elementi atomici che hanno un significato per il linguaggio.
12.1 Premessa
12.1.1 Elementi semantici
Per dare un senso, anche intuitivo, agli esempi presentati assieme ai concetti sintattici che saranno introdotti nel seguito, è opportuno definire, informalmente, alcuni elementi semantici, a partire da nozioni di base:
Variabile: È un nome dato ad un valore presente in memoria.
Espressione: È una combinazione di elementi di sintassi che può essere valutata per produrre un valore. In altre parole, un’espressione è un insieme di elementi come letterali, variabili, accessi ad attributi, operatori o chiamate di funzione che restituiscono tutti un valore.
Funzione: È un insieme di istruzioni che può essere parametrizzato da una serie di input predefiniti e può avere una serie di output, a cui è associato un nome.
Classe: È una definizione che raggruppa un insieme di attributi e operazioni che agiscono sugli attributi della propria o di altre classi.
Oggetto: È un’istanza di una classe, cioè un particolare valore di una classe che è stato creato in memoria.
Modulo: È un file contenente definizioni di variabili, funzioni e classi che possono essere importate e utilizzate in altri programmi o moduli.
12.1.2 Le funzioni print()
e help()
Nel seguito useremo molto la funzione print()
che permette di visualizzare informazioni sullo schermo. Ciò è fondamentale per capire cosa sta succedendo nel programma in modo immediato ed è una semplice alternativa a strumenti di ispezione o debugging messi a diposizione da IDE.
Usare print()
è facile: basta scrivere print(espressione)
e mettere nelle parentesi una espressione
che si vuole visualizzare.
Ad esempio, per stampare "Ciao, mondo!"
, si scrive:
Nel REPL, invece, ciò si ottiene semplicemente dando l’invio:
Una seconda funzione molto utile è help()
. Fornisce informazioni sulla documentazione di moduli, funzioni, classi e metodi.
Ad esempio, per ottenere informazioni sulla funzione print()
, possiamo scrivere:
1>>> help(print)
- 1
-
Output di
help(print)
:
Help on built-in function print in module builtins:
print(*args, sep=' ', end='\n', file=None, flush=False)
Prints the values to a stream, or to sys.stdout by default.
sep
string inserted between values, default a space.
end
string appended after the last value, default a newline.
file
a file-like object (stream); defaults to the current sys.stdout.
flush
whether to forcibly flush the stream.
Nel REPL offre la massima utilità, perché eseguendo help()
si passa dalla modalità interattiva del REPL a quella di navigazione del contenuto testuale della risposta, funzionalità molto comoda soprattutto per testi lunghi. Per ritornare al REPL, basta premere q
e poi Invio
.
12.2 Righe
Le righe sono di due tipi: logiche e fisiche. Le seconde sono le più facilmente individuabili nel testo di un programma, perché sono terminate da un carattere di a capo. Una o più righe fisiche costituiscono una riga logica che corrisponde ad una istruzione. Esiste una eccezione, poco usata e consigliata in Python, per cui una riga fisica contiene più istruzioni separate da ;
.
Vi sono due modi per dividere una riga logica in righe fisiche. Il primo è terminare con il backslash (\
, poco usata la traduzione barra rovesciata o simili) tutte le righe fisiche meno l’ultima (intendendo con ciò che il backslash precede l’a capo):
- 1
- L’istruzione di assegnamento è spezzata su due righe fisiche.
- 2
- L’istruzione condizionale ha due espressioni che devono essere entrambe vere, ognuna su una riga fisica.
- 3
-
Non importa quanto sono indentate le righe fisiche successive alla prima e ciò può essere sfruttato per incrementare la leggibilità, ad esempio, allineando le espressioni
x > 5
ex < 9
in colonna.
Il secondo è per mezzo di parentesi, giacché tutte le righe fisiche che seguono una con parentesi tonda (
, quadra [
o graffa {
aperta, fino a quella con l’analoga parentesi chiusa, sono unite in una logica. Le regole di indentazione, che vedremo nel seguito, si applicano solo alla prima riga fisica.
Esempi sintatticamente corretti ma sconsigliabili, per l’inerente illeggibilità:
- 1
- L’espressione è spezzata su due righe fisiche e le parentesi tonde rappresentano un’alternativa all’uso del backslash.
- 2
- Le righe fisiche della lista non hanno la stessa indentazione e una espressione è spezzata su due righe.
- 3
- La lista è spezzata su due righe fisiche e un delimitatore inizia la riga anziché terminare la precedente.
12.3 Commenti
Un commento inizia con un carattere cancelletto (#
) e termina alla fine della riga fisica. I commenti non possono coesistere con il backslash come separatore di riga logica, giacché entrambi devono chiudere la riga fisica.
Esempi non sintatticamente corretti:
- 1
-
Il backslash deve terminare la riga fisica, quindi non può essere seguito da un commento. Se necessario può andare o alla riga successiva, scelta consigliata, o alla precedente. L’interprete segnalerà l’errore
SyntaxError
. - 2
-
Il commento rende il backslash parte di esso quindi non segnala più la fine della riga fisica e, all’esecuzione, si avrà anche qui un errore di tipo
SyntaxError
, perchéand
deve essere seguito da un’espressione.
12.4 Indentazione
Indentazione significa che spazi o, in alternativa, tabulazioni precedono un carattere che non sia nessuno dei due. Il numero di spazi, ottenuto dopo la trasformazione delle tabulazioni in spazi, si definisce livello di indentazione.
L’indentazione del codice è il modo che Python utilizza per raggruppare le istruzioni in un blocco, ove tutte devono presentare la medesima indentazione. La prima riga logica che ha una indentazione minore della precedente, segnala che il blocco è stato chiuso proprio da quest’ultima. Anche le clausole di un’istruzione composta devono avere la stessa indentazione.
La prima istruzione di un file o la prima inserita al prompt >>>
del REPL non deve presentare spazi o tabulazioni, cioè ha un livello di indentazione pari a 0.
Alcuni esempi:
Definizione di una funzione:
- 1
- Prima riga senza indentazione.
- 2
- Questa riga e la successiva appartengono allo stesso blocco e, pertanto, hanno la medesima indentazione.
Test di condizione:
x = 10 1if x < 0: 2 print("x è negativo") elif x == 0: print("x è zero") else: print("x è positivo")
- 1
-
Le tre clausole
if
,then
eelse
della medesima istruzione composta di test di condizione, devono avere identica indentazione. - 2
- I tre blocchi hanno come unico vincolo quello di avere un livello maggiore di indentazione della riga precedente. I blocchi corrispondenti alle diverse clausole non devono avere lo stesso livello di indentazione, anche se è buona prassi farlo.
Non si possono avere sia spazi che tabulazioni per definire il livello di indentazione nello stesso file. Ciò perché renderebbe ambiguo il numero di spazi che si ottiene dopo la trasformazione delle tabulazioni in spazi. Quindi, o si usano spazi, scelta raccomandata, o tabulazioni.
12.5 Token
Le righe logiche sono composte da token che si categorizzano in parole chiave, identificatori, operatori, delimitatori e letterali. I token sono separati da un numero arbitrario di spazi e tabulazioni. Ad esempio:
12.5.1 Identificatori
Un identificatore è un nome assegnato ad un oggetto, cioè una variabile, una funzione, una classe, un modulo e altro. Esso è case sensitive cioè python
e Python
sono due identificatori diversi.
Alcuni esempi:
1intero = 42
2decimale = 3.14
3testo = "Ciao, mondo!"
4lista = [1, 2, 3]
5dizionario = {"chiave": "valore"}
6def mia_funzione():
print("Questa è una funzione")
7class MiaClasse:
8 def __init__(self, valore):
9 self.valore = valore
10 def metodo(self):
print("Questo è un metodo della classe")
11import math
12def mio_generatore():
yield 1
yield 2
yield 3
13mio_oggetto = MiaClasse(10)
- 1
-
Identificatore di numero intero:
intero
. - 2
-
Identificatore di numero decimale:
decimale
. - 3
-
Identificatore di stringa:
testo
. - 4
-
Identificatore di lista:
lista
. - 5
-
Identificatore di dizionario:
dizionario
. - 6
-
Identificatore di funzione:
mia_funzione
. - 7
-
Identificatore di classe:
MiaClasse
. - 8
-
Identificatore di metodo e parametro:
__init__
evalore
. - 9
-
Identificatore di attributo:
valore
. - 10
-
Identificatore di metodo:
metodo
. - 11
-
Identificatore di modulo:
math
. - 12
-
Identificatore di generatore:
mio_generatore
. - 13
-
Identificatore di oggetto:
mio_oggetto
.
12.5.2 Parole chiave
Le parole chiave sono parole che non possono essere usate per scopi diversi da quelli predefiniti nel linguaggio e, quindi, non possono essere usate come identificatori. Ad esempio, True
che rappresenta il valore logico di verità, non può essere usato per definire ad esempio una variabile.
Esistono anche delle parole chiave contestuali, cioè che sono tali solo in alcuni contesti ed altrove possono essee usate come identificatori. Usiamo il codice seguente per ottenere una lista di parole chiave e parole chiave contestuali:
1import keyword
2parole_chiave = keyword.kwlist
3parole_chiave_contestuale = keyword.softkwlist
4print(parole_chiave)
5print(parole_chiave_contestuale)
- 1
-
Importa il modulo
keyword
. - 2
- Ottiene la lista delle parole chiave.
- 3
- Ottiene la lista delle parole chiave contestuali.
- 4
- Stampa la lista delle parole chiave.
- 5
- Stampa la lista delle parole chiave contestuali.
Nella tabella seguente invece un elenco completo con breve descrizione:
Parola chiave | Descrizione |
---|---|
Valori booleani | |
False |
Valore booleano falso |
True |
Valore booleano vero |
Operatori logici | |
and |
Operatore logico AND |
or |
Operatore logico OR |
not |
Operatore logico NOT |
Operatori di controllo di flusso | |
if |
Utilizzato per creare un’istruzione condizionale |
elif |
Utilizzato per aggiungere condizioni in un blocco if |
else |
Utilizzato per specificare il blocco di codice da eseguire se le condizioni precedenti sono false |
for |
Utilizzato per creare un ciclo for |
while |
Utilizzato per creare un ciclo while |
break |
Interrompe il ciclo in corso |
continue |
Salta l’iterazione corrente del ciclo e passa alla successiva |
pass |
Indica un blocco di codice vuoto |
return |
Utilizzato per restituire un valore da una funzione |
Gestione delle eccezioni | |
try |
Utilizzato per definire un blocco di codice da eseguire e gestire le eccezioni |
except |
Utilizzato per catturare le eccezioni in un blocco try-except |
finally |
Blocco di codice che viene eseguito alla fine di un blocco try, indipendentemente dal fatto che si sia verificata un’eccezione |
raise |
Utilizzato per sollevare un’eccezione |
Definizione delle funzioni e classi | |
def |
Utilizzato per definire una funzione |
class |
Utilizzato per definire una classe |
lambda |
Utilizzato per creare funzioni anonime |
Gestione contesto di dichiarazione di variabili | |
global |
Utilizzato per dichiarare variabili globali |
nonlocal |
Utilizzato per dichiarare variabili non locali |
Operazioni su moduli | |
import |
Utilizzato per importare moduli |
from |
Utilizzato per importare specifici elementi da un modulo |
as |
Utilizzato per creare alias, ad esempio negli import |
Operatori di identità e appartenenza | |
in |
Utilizzato per verificare se un valore esiste in una sequenza |
is |
Operatore di confronto di identità |
Gestione delle risorse | |
with |
Utilizzato per garantire un’azione di pulizia come il rilascio delle risorse |
Programmazione asincrona | |
async |
Utilizzato per definire funzioni asincrone |
await |
Utilizzato per attendere un risultato in una funzione asincrona |
Varie | |
del |
Utilizzato per eliminare oggetti |
assert |
Utilizzato per le asserzioni, verifica che un’espressione sia vera |
yield |
Utilizzato per restituire un generatore da una funzione |
None |
Rappresenta l’assenza di valore o un valore nullo |
Parole chiave contestuali | |
match |
Utilizzato nell’istruzione match per il pattern matching |
case |
Utilizzato nell’istruzione match per definire un ramo |
_ |
Utilizzato come identificatore speciale nell’istruzione match per indicare un pattern di default o ignorare valori |
type |
Utilizzato in specifici contesti per dichiarazioni di tipo |
Esempi di uso di parole chiave contestuali:
match
,case
e_
:1def process_value(value): 2 match value: 3 case 1: print("Uno") case 2: print("Due") 4 case _: print("Altro") 5match = "Questo è un identificatore valido" process_value(1) # Output: Uno process_value(2) # Output: Due process_value(3) # Output: Altro 6print(match)
- 1
- Definiamo una funzione che utilizza il pattern matching.
- 2
-
Uso di
match
come parola chiave. - 3
-
Uso di
case
come parola chiave. - 4
-
Uso di
_
come parola chiave. - 5
-
Utilizzo di
match
come identificatore per una variabile. - 6
-
Output:
Questo è un identificatore valido
.
type
:from typing import TypeAlias 1type Point = tuple[float, float] 2def distanza(p1: Point, p2: Point) -> float: return ((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) ** 0.5 3punto1: Point = (1.0, 2.0) punto2: Point = (4.0, 6.0) 4print(distanza(punto1, punto2)) 5print(type(punto1))
- 1
-
Uso di
type
come parola chiave. - 2
-
Utilizzo dell’alias di tipo
Point
per i parametrip1
ep2
della funzione. - 3
-
Definizione delle variabili
punto1
epunto2
comePoint
utilizzando l’alias di tipo. Questo rende chiaro che entrambe le variabili sono tuple di duefloat
, anche se l’interprete non controlla la coerenza tra oggetto e alias di tipo. - 4
-
Output:
5.0
. - 5
-
Uso di
type
identificatore di una funzione. Output:<class 'tuple'>
.
12.5.3 Classi riservate di identificatori
Alcune classi di identificatori (oltre alle parole chiave) hanno significati speciali in Python. Queste classi sono identificate dai pattern di caratteri di sottolineatura (underscore) all’inizio e alla fine dei nomi. Tuttavia, l’uso di questi identificatori non impone limitazioni rigide al programmatore, ma è importante seguire le convenzioni per evitare ambiguità e problemi di compatibilità.
Identificatori speciali:
_
:- Non importato da
from module import *
: Gli identificatori che iniziano con un singolo underscore non vengono importati con un’istruzione di importazione globale. Questo è un meccanismo per indicare che tali variabili o funzioni sono destinate ad essere private al modulo e non dovrebbero essere usate direttamente da altri moduli. Esempio:
In un file
example.py
:1_private_variable = "Variabile da non esportare'"
- 1
-
Nel modulo
example.py
viene definita la variabile come privata.
In altro modulo diverso da
example.py
:- 1
-
Dal modulo
example
vengono importatati tutti gli identificatori pubblici. - 2
-
Genera un errore:
NameError: name '_private_variable' is not defined
.
Pattern nei match: Nel contesto di un pattern di corrispondenza all’interno di un’istruzione
match
,_
è una parola chiave contestuale che denota un wildcard (carattere jolly), coem indicato sopra.Interprete interattivo: L’interprete interattivo rende disponibile il risultato dell’ultima valutazione nella variabile
_
. Il valore di_
è memorizzato nel modulobuiltins
, insieme ad altre funzioni e variabili predefinite comeprint()
, permettendo l’accesso globale a_
durante una sessione interattiva. Esempio:# Eseguire nel REPL result = 5 + 3 1print(_)
- 1
-
Output:
8
(nell’interprete interattivo).
Altro uso: Altrove,
_
è un identificatore regolare. Viene spesso usato per nominare elementi speciali per l’utente, ma non speciali per Python stesso. Il nome_
è comunemente usato in congiunzione con l’internazionalizzazione (vedi la documentazione del modulogettext
per ulteriori informazioni su questa convenzione) ed è anche comunemente utilizzato per variabili non usate. Esempio:- 1
-
Uso di
_
come variabile regolare. - 2
-
Uso di
_
per internazionalizzazione.
- Non importato da
__*__
: Questi nomi, informalmente noti come nomi dunder1, sono definiti dall’interprete e dalla sua implementazione (inclusa la libreria standard). Altri potrebbero essere definiti nelle versioni future di Python. Qualsiasi uso di nomi__*__
, in qualsiasi contesto, che non segua l’uso esplicitamente documentato, è soggetto a discontinuazione senza preavviso. Esempio:class MyClass: 1 def __init__(self, value): self.__value = value def __str__(self): 2 return f"MyClass con valore {self.__value}" obj = MyClass(10) 3print(obj)
- 1
- Dunder per metodo chiamato alla creazione dell’oggetto.
- 2
-
Dunder chiamato da
print
con parametro l’oggetto. - 3
-
Output:
MyClass
con valore10
.
__*
: I nomi in questa categoria, quando utilizzati all’interno di una definizione di classe, vengono riscritti dal compilatore (processo noto come name mangling) per evitare conflitti di nome tra attributi “privati” delle classi base e delle classi derivate. Questo aiuta a garantire che gli attributi destinati ad essere privati non vengano accidentalmente sovrascritti nelle sottoclassi. Esempio:class BaseClass: def __init__(self): self.__private_attr = "Base" class DerivedClass(BaseClass): def __init__(self): super().__init__() self.__private_attr = "Derived" base_obj = BaseClass() derived_obj = DerivedClass() 1print(base_obj._BaseClass__private_attr) 2print(derived_obj._DerivedClass__private_attr)
- 1
-
Accesso al nome di
BaseClass
. - 2
-
Accesso al nome di
DerivedClass
.
1 I nomi con doppio underscore (__
) sono chiamati dunder come abbreviazione di double underscore.
12.5.4 Operatori
Gli operatori sono rappresentati da simboli non alfanumerici e, quando applicati a uno o più identificatori, letterali o espressioni (definiti genericamente operandi), producono un valore di risultato. Attenzione a non confondere la definizione di operatore come token, considerata qui, con quella di operatore come funzionalità algoritmica, poiché alcune parole chiave sono operatori algoritmici e anche le funzioni possono agire come operatori.
Esempi:
- 1
-
Utilizza l’operatore
+
sugli identificatorix
ey
. - 2
-
Utilizza l’operatore
+
su letterali numerici. - 3
- Utilizza vari operatori su espressioni.
In tabella l’elenco degli operatori:
Operatore | Descrizione |
---|---|
Aritmetici | |
+ |
Addizione |
- |
Sottrazione |
* |
Moltiplicazione |
/ |
Divisione |
// |
Divisione intera |
% |
Modulo |
** |
Esponenziazione |
@ |
Matrice (operatore di moltiplicazione) |
Di confronto | |
< |
Minore |
> |
Maggiore |
<= |
Minore o uguale |
>= |
Maggiore o uguale |
== |
Uguale |
!= |
Diverso |
Sui bit | |
& |
AND bit a bit |
| |
OR bit a bit |
^ |
XOR bit a bit |
~ |
NOT bit a bit |
<< |
Shift a sinistra |
>> |
Shift a destra |
Di assegnazione | |
:= |
Operatore di assegnazione in espressione (walrus o tricheco) |
Una caratteristica molto potente del linguaggio è che gli operatori sono utilizzabili anche sui tipi creati dall’utente con logica determinata dalla implementazione. Pertanto, non vanno visti come limitati alle espressioni artimetiche o di assegnazione sui letterali definiti dal linguaggio, ma anche sugli identificatori corrispondenti a tipi più complessi, anche definiti nel programma o importati.
Un esempio è dato dall’operatore @
che, nel codice seguente, è applicato ad un nuovo tipo di oggetto corrispondente al concetto matematico di matrice. La classe Matrice
presenta gli attributi per memorizzare gli elementi numerici e anche l’implementazione dell’operazione matematica di moltiplicazione, nel metodo __matmul__
. Quando l’interprete trova @
, chiama __matmul__
dell’operando di sinistra e gli passa l’oggetto corrispondente all’operando di destra.
Codice:
class Matrice:
def __init__(self, righe):
self.righe = righe
self.num_righe = len(righe)
self.num_colonne = len(righe[0]) if righe else 0
1 def __matmul__(self, altra):
2 if self.num_colonne != altra.num_righe:
raise ValueError("Non è possibile moltiplicare le matrici: "
"dimensioni incompatibili.")
3 risultato = [[0 for _ in range(altra.num_colonne)]
for _ in range(self.num_righe)]
4 for i in range(self.num_righe):
for j in range(altra.num_colonne):
for k in range(self.num_colonne):
risultato[i][j] += (self.righe[i][k] *
altra.righe[k][j])
return Matrice(risultato)
5 def __repr__(self):
return '\n'.join([' '.join(map(str, riga)) for riga in self.righe])
6A = Matrice([[1, 2],
[3, 4]])
7B = Matrice([[5, 6],
[7, 8]])
8C = A @ B
print("Matrice A:")
9print(A)
print("Matrice B:")
print(B)
print("Risultato di A @ B:")
10print(C)
- 1
-
Metodo di implementazione della moltiplicazione di matrici, chiamato da
@
usato come operatore su oggetti di tipoMatrice
. - 2
- Controlla se le dimensioni sono compatibili per la moltiplicazione.
- 3
- Inizializza la matrice risultato con zeri.
- 4
- Esegue la moltiplicazione delle matrici.
- 5
-
Chiamato da
print()
quando applicato su oggetti di tipoMatrice
. - 6
- Rappresentazione leggibile della matrice operando di sinistra.
- 7
- Rappresentazione leggibile della matrice operando di destra.
- 8
-
Moltiplicazione di matrici utilizzando l’operatore
@
che chiama__matmul__()
per il calcolo effettivo. - 9
-
Chiama
__repr__()
diMatrice
per ottenere la stringa su due righe perA
:1 2
e3 4
. - 10
-
Chiama
__repr__()
diMatrice
per ottenere la stringa su due righe perC
:19 22
e43 50
.
12.5.5 Delimitatori
In Python, alcuni token servono come delimitatori nella grammatica del linguaggio. I delimitatori sono caratteri che separano le varie componenti del codice, come espressioni, blocchi di codice, parametri di funzioni e istruzioni.
La seguente tabella include tutti i delimitatori e i principali utilizzi:
Delimitatore | Descrizione |
---|---|
( |
Utilizzata per raggruppare espressioni, chiamate di funzione e definizioni di tupla |
) |
Utilizzata per chiudere le parentesi tonde aperte |
[ |
Utilizzate per definire liste e accedere agli elementi delle liste, tuple, o stringhe |
] |
Utilizzate per chiudere le parentesi quadre aperte |
{ |
Utilizzate per definire dizionari e set |
} |
Utilizzate per chiudere le parentesi graffe aperte |
, |
Utilizzata per separare elementi in liste, tuple, e argomenti nelle chiamate di funzione |
: |
Utilizzato per definire blocchi di codice (come in if , for , while , def , class ) e per gli slice |
. |
Utilizzato per accedere agli attributi di un oggetto. Può apparire in letterali decimnali e immaginari |
; |
Utilizzato per separare istruzioni multiple sulla stessa riga |
@ |
Utilizzato per dichiarare decoratori per funzioni e metodi |
= |
Operatore utilizzato per assegnare valori a variabili |
-> |
Annotazione del tipo di ritorno delle funzioni |
+= |
Assegnazione aumentata con addizione. Aggiunge il valore a destra a quello a sinistra e assegna il risultato alla variabile a sinistra. Come i successivi, è sia un delimitatore che un operatore |
-= |
Assegnazione aumentata con sottrazione |
*= |
Assegnazione aumentata con moltiplicazione |
/= |
Assegnazione aumentata con divisione |
//= |
Assegnazione aumentata con divisione intera |
%= |
Assegnazione aumentata con modulo |
@= |
Assegnazione aumentata con moltiplicazione di matrici |
&= |
Assegnazione aumentata con AND bit a bit |
|= |
Assegnazione aumentata con OR bit a bit |
^= |
Assegnazione aumentata con XOR bit a bit |
>>= |
Assegnazione aumentata con shift a destra |
<<= |
Assegnazione aumentata con shift a sinistra |
**= |
Assegnazione aumentata con esponenziazione |
Una sequenza di tre punti, comunemente indicata come ellissi anche al difuori dei linguaggi di programmazione,2 è trattata come un token a sé e corrisponde ad un oggetto predefinito chiamato Ellipsis, con applicazioni in diversi contesti:
2 L’ellissi è usata, ad esempio, in C per dichiarare funzioni che accettano un numero variaible di parametri e i Javascript come operatore per espandere gli array o le proprietà di un oggetto.
1print(type(...))
def funzione_da_completare():
2 ...
class ClasseEsempio:
def metodo_da_completare(self):
...
from typing import Callable
3def funzione_variadica(func: Callable[..., int]):
pass
import numpy as np
array = np.array([[[1, 2, 3], [4, 5, 6]],
[[7, 8, 9], [10, 11, 12]]])
4print(array[..., 1])
- 1
-
Otteniamo il tipo dell’oggetto ellissi. L’output è
<class 'ellipsis'>
. - 2
-
Utilizzo come segnaposto per indicare che la funzione è da completare. Da notare che chiamare la funzione
funzione_da_completare()
non dà errore. - 3
-
L’uso di
Callable[..., int]
indica una funzione che può accettare un numero variabile di argomenti di qualsiasi tipo e restituire un valore di tipo int. - 4
-
numpy
è una libreria di calcolo matriciale molto diffusa. L’ellissi è utilizzata per effettuare uno sezione complessa della matrice secondo tutte le dimensioni precedenti all’ultima. In altre parole, l’ellissi permette di selezionare interamente tutte le dimensioni tranne l’ultima specificata. Il risultato stampato in console è su due righe:[[ 2 5]
e[ 8 11]]
.
Alcuni caratteri ASCII hanno un significato speciale come parte di altri token o sono significativi per l’analizzatore lessicale:
Carattere | Descrizione |
---|---|
' |
Utilizzato per definire stringhe di caratteri. |
" |
Utilizzato per definire stringhe di caratteri. |
# |
Simbolo di commento. Utilizzato per indicare un commento, che viene ignorato dall’interprete Python. |
\ |
Backslash. Utilizzato per caratteri di escape nelle stringhe e per continuare le righe di codice su più righe fisiche. |
Alcuni caratteri ASCII non sono utilizzati in Python e la loro presenza al difuori dei letterali di stringa e dei commenti genera un errore: $
, ?
, `
.
12.5.6 Letterali
I letterali sono notazioni per valori costanti di alcuni tipi predefiniti nel linguaggio. Esistono diversi tipi di letterali, ognuno rappresenta un tipo di dato specifico e ha una sintassi particolare.
12.5.6.1 Numerici
I letterali numerici includono interi, numeri a virgola mobile e numeri immaginari:
Interi, possono essere scritti in base decimale, ottale, esadecimale o binaria:
- Decimale:
42
. - Ottale:
0o12
,0O7
. - Esadecimale:
0xA
,0X1F
. - Binario:
0b1010
,0B11
.
- Decimale:
Virgola mobile, possono essere rappresentati con una parte intera e una decimale, oppure con notazione scientifica:
- Virgola mobile:
3.14
,0.001
. - Notazione scientifica:
1e10
,2.5E-3
.
- Virgola mobile:
Numeri immaginari, ottenuti da un letterale intero o a virgola mobile con un suffisso
j
oJ
:4j
,4.j
,3.14e-10j
.
12.5.6.2 Stringhe
I letterali di stringa possono essere racchiusi tra virgolette singole o doppie. Possono anche essere multi-linea se racchiusi tra triple virgolette singole o doppie:
Stringhe racchiuse tra virgolette singole o doppie:
- Singole:
'ciao'
. - Doppie:
"mondo"
.
- Singole:
Stringhe multi-linea racchiuse tra triple virgolette singole o doppie:
- Triple singole:
'''testo multi-linea'''
. - Triple doppie:
"""testo multi-linea"""
.
- Triple singole:
Le stringhe tra tripli apici possono avere degli a capo e degli apici (non tripli) all’interno.
Esempio:
Tutte le stringhe sono codificate in Unicode, con il prefisso b
la stringa è di tipo byte ed è limitata ai 128 caratteri dell’ASCII. Se si prepone r
, che sta per raw cioè grezzo, allora la codifica è sempre Unicode ma i caratteri di escape3 non sono intepretati.
3 In Python, il carattere di escape \
è utilizzato nelle stringhe per inserire caratteri speciali che non possono essere facilmente digitati sulla tastiera o che hanno significati speciali.
Alcuni esempi comuni includono:
\n
per una nuova linea (linefeed).\t
per una tabulazione.\\
per inserire un backslash.\'
per il singolo apice.\"
per i doppi apici.
12.5.6.3 F-stringhe
Le f-stringhe (stringhe formattate) sono racchiuse tra virgolette singole, doppie o triple e sono precedute dal prefisso f
o F
. Il formattato nel nome deriva dalla possibilità di includere espressioni Python all’interno che sono valutate all’atto della esecuzione della istruzione contenente la stringa. Si possono avere stringhe formattate grezze ma non byte.
Esempio:
nome = "Python"
1f_stringa = f'Ciao, {nome.upper()}!'
definizione = "Linguaggio"
f_stringa_multi_linea = f'''Questo è un esempio
di f-stringa multi-linea
2in {definizione.lower() + ' ' + nome}'''
3print(f_stringa)
4print(f_stringa_multi_linea)
- 1
-
Viene chiamato il metodo della stringa
lower()
per avere il maiuscolo. - 2
-
Usiamo un’espressione di concatenazione di stringhe colla prima generata da un metodo che mette in minuscolo la stringa di
definizione
. - 3
-
Output:
Ciao, python!
. - 4
-
Output composto dalle tre righe
Questo è un esempio
,di f-stringa multi-linea
ein linguaggio Python
.