Sintassi e semantica Python - Python syntax and semantics
La sintassi del linguaggio di programmazione Python è l'insieme di regole che definisce come verrà scritto e interpretato un programma Python (sia dal sistema runtime che dai lettori umani). Il linguaggio Python ha molte somiglianze con Perl , C e Java . Tuttavia, ci sono alcune differenze definite tra le lingue.
Filosofia del design
Python è stato progettato per essere un linguaggio altamente leggibile . Ha un layout visivo relativamente ordinato e usa frequentemente parole chiave inglesi dove altre lingue usano la punteggiatura . Python mira ad essere semplice e coerente nella progettazione della sua sintassi, incapsulata nel mantra "Ci dovrebbe essere uno—e preferibilmente solo un—modo ovvio per farlo", dallo Zen di Python .
Questo mantra si oppone deliberatamente al mantra Perl e Ruby , " c'è più di un modo per farlo ".
Parole chiave
Python ha 35 parole chiave o parole riservate ; non possono essere utilizzati come identificatori .
andasassertasyncawaitbreakclasscontinuedefdelelifelseexceptFalsefinallyforfromglobalifimportinislambdaNonenonlocalnotorpassraisereturnTruetrywhilewithyield
- Appunti
rientro
Python usa gli spazi per delimitare i blocchi del flusso di controllo (seguendo la regola del fuorigioco ). Python prende in prestito questa caratteristica dal suo predecessore ABC : invece della punteggiatura o delle parole chiave, usa l'indentazione per indicare l'esecuzione di un blocco .
Nei cosiddetti linguaggi a "formato libero", che utilizzano la struttura a blocchi derivata da ALGOL, i blocchi di codice sono contrassegnati da parentesi graffe ( { }) o parole chiave. Nella maggior parte delle convenzioni di codifica per questi linguaggi, i programmatori indentano convenzionalmente il codice all'interno di un blocco, per distinguerlo visivamente dal codice circostante.
Una funzione ricorsiva denominata , a cui viene passato un singolo parametro , , e se il parametro è 0 chiamerà una funzione diversa denominata e altrimenti chiamerà , passando , e si chiamerà anche ricorsivamente, passando come parametro, potrebbe essere implementata in questo modo in Python :
fooxbarbazxx-1
def foo(x):
if x == 0:
bar()
else:
baz(x)
foo(x - 1)
e potrebbe essere scritto in questo modo in C con lo stile di rientro K&R :
void foo(int x)
{
if (x == 0) {
bar();
} else {
baz(x);
foo(x - 1);
}
}
Python impone una convenzione che i programmatori in linguaggi in stile ALGOL spesso seguono.
Il codice indentato in modo errato potrebbe essere letto male da un lettore umano in modo diverso da come sarebbe interpretato da un compilatore o un interprete. Questo esempio illustra un errore introdotto da un rientro errato:
def foo(x):
if x == 0:
bar()
else:
baz(x)
foo(x - 1)
Qui, contrariamente fooall'esempio precedente , la chiamata di funzione foo(x - 1)sull'ultima riga è erroneamente rientrata per essere al di fuori del blocco if/ else. Ciò causerebbe l'esecuzione sempre, anche quando xè 0, risultando in una ricorsione infinita .
Sebbene sia lo spazio che i caratteri di tabulazione siano accettati come forme di rientro e sia possibile utilizzare qualsiasi multiplo di spazi, gli spazi sono consigliati e 4 spazi (come in questo articolo) sono consigliati e sono di gran lunga i più comunemente usati. Non è consentito mescolare spazi e tabulazioni su righe consecutive nello stesso file di codice sorgente a partire da Python 3 perché ciò può creare bug difficili da vedere, poiché molti strumenti non distinguono visivamente spazi e tabulazioni.
Strutture dati
Poiché Python è un linguaggio tipizzato dinamicamente , i valori Python , non le variabili, contengono informazioni sul tipo. Ciò ha implicazioni per molti aspetti del funzionamento del linguaggio.
Tutte le variabili in Python contengono riferimenti a oggetti e questi riferimenti vengono passati alle funzioni; una funzione non può modificare il valore dei riferimenti variabili nella sua funzione chiamante (ma vedi sotto per le eccezioni). Alcune persone (incluso lo stesso Guido van Rossum ) hanno chiamato questo schema di passaggio dei parametri "chiamata per riferimento all'oggetto". Un riferimento a un oggetto significa un nome, e il riferimento passato è un "alias", cioè una copia del riferimento allo stesso oggetto, proprio come in C/ C++ . Il valore dell'oggetto può essere modificato nella funzione chiamata con l'"alias", ad esempio:
>>> alist = ['a', 'b', 'c']
>>> def my_func(al):
... al.append('x')
... print(al)
...
>>> my_func(alist)
['a', 'b', 'c', 'x']
>>> alist
['a', 'b', 'c', 'x']
La funzione ha my_funccambiato il valore di alistcon l'argomento formale al, che è un alias di alist. Tuttavia, qualsiasi tentativo di operare sull'alias stesso non avrà alcun effetto sull'oggetto originale. In Python, i nomi accessibili non-locali più interni e non-dichiarati globali sono tutti alias.
Tra i linguaggi tipizzati dinamicamente, Python è moderatamente controllato dal tipo. La conversione implicita è definita per i tipi numerici (così come per i booleani ), quindi si può validamente moltiplicare un numero complesso per un intero (ad esempio) senza cast esplicito. Tuttavia, non esiste una conversione implicita tra, ad esempio, numeri e stringhe ; una stringa è un argomento non valido per una funzione matematica che si aspetta un numero.
Tipi di base
Python ha una vasta gamma di tipi di dati di base. Oltre all'aritmetica convenzionale in virgola mobile e intera , supporta in modo trasparente l'aritmetica a precisione arbitraria , i numeri complessi e i numeri decimali .
Python supporta un'ampia varietà di operazioni sulle stringhe. Le stringhe in Python sono immutabili , quindi un'operazione di stringa come una sostituzione di caratteri , che in altri linguaggi di programmazione potrebbe alterare la stringa in atto , restituisce una nuova stringa in Python. Le considerazioni sulle prestazioni a volte spingono per l'utilizzo di tecniche speciali nei programmi che modificano le stringhe in modo intensivo, come l'unione di matrici di caratteri in stringhe solo se necessario.
Tipi di raccolta
Uno degli aspetti molto utili di Python è il concetto di tipi di raccolta (o contenitore ) . In generale, una raccolta è un oggetto che contiene altri oggetti in un modo facilmente referenziato o indicizzato . Le raccolte sono disponibili in due forme fondamentali: sequenze e mappature .
I tipi sequenziali ordinati sono elenchi ( array dinamici ), tuple e stringhe. Tutte le sequenze sono indicizzate in modo posizionale ( da 0 a lunghezza − 1 ) e tutte tranne le stringhe possono contenere qualsiasi tipo di oggetto, inclusi più tipi nella stessa sequenza. Sia le stringhe che le tuple sono immutabili, il che le rende candidate perfette per le chiavi del dizionario (vedi sotto). Gli elenchi, invece, sono mutevoli; gli elementi possono essere inseriti, eliminati, modificati, aggiunti o ordinati sul posto .
I mapping, d'altra parte, sono tipi (spesso non ordinati) implementati sotto forma di dizionari che "mappano" un insieme di chiavi immutabili agli elementi corrispondenti (proprio come una funzione matematica). Ad esempio, si potrebbe definire un dizionario con una stringa "toast"mappata sull'intero 42o viceversa. Le chiavi in un dizionario devono essere di un tipo Python immutabile, come un intero o una stringa, perché dietro le quinte sono implementate tramite una funzione hash . Ciò rende i tempi di ricerca molto più rapidi, ma richiede che le chiavi non vengano modificate.
I dizionari sono fondamentali per l'interno di Python poiché risiedono al centro di tutti gli oggetti e le classi: le mappature tra i nomi delle variabili (stringhe) e i valori a cui i nomi fanno riferimento sono memorizzati come dizionari (vedi Sistema di oggetti ). Poiché questi dizionari sono direttamente accessibili (tramite l' __dict__attributo di un oggetto ), la metaprogrammazione è un processo semplice e naturale in Python.
Un tipo di raccolta di insiemi è una raccolta non indicizzata e non ordinata che non contiene duplicati e implementa operazioni teoriche sugli insiemi come unione , intersezione , differenza , differenza simmetrica e test di sottoinsiemi . Ci sono due tipi di insiemi: sete frozenset, l'unica differenza è che setè mutabile ed frozensetè immutabile. Gli elementi in un set devono essere hashable. Così, ad esempio, a frozensetpuò essere un elemento di un regolare setmentre non è vero il contrario.
Python fornisce anche ampie capacità di manipolazione della raccolta come il controllo del contenimento integrato e un protocollo di iterazione generico.
Sistema di oggetti
In Python, tutto è un oggetto, anche le classi. Le classi, in quanto oggetti, hanno una classe, nota come metaclasse . Python supporta anche l' ereditarietà multipla e i mixin .
Il linguaggio supporta un'ampia introspezione di tipi e classi. I tipi possono essere letti e confrontati: i tipi sono istanze di type. Gli attributi di un oggetto possono essere estratti come dizionario.
Gli operatori possono essere sovraccaricati in Python definendo funzioni membro speciali - per esempio, definendo un metodo chiamato __add__su una classe si permette di usare l' +operatore su oggetti di quella classe.
letterali
stringhe
Python ha vari tipi di stringhe letterali .
Letterali stringa normali
È possibile utilizzare virgolette singole o doppie per citare le stringhe. A differenza dei linguaggi shell Unix, i linguaggi Perl o influenzati da Perl come Ruby o Groovy , apici singoli e doppi apici funzionano in modo identico, cioè non c'è interpolazione di stringhe delle espressioni $foo . Tuttavia, l'interpolazione può essere eseguita in vari modi: con "f-strings" (a partire da Python 3.6), utilizzando il formatmetodo o il vecchio operatore % string-format.
Ad esempio, l'istruzione Perl:
print "I just printed $num pages to the printer $printer\n"
è equivalente a una di queste istruzioni Python:
print(f"I just printed {num} pages to the printer {printer}")
print("I just printed {} pages to the printer {}".format(num, printer))
print("I just printed {0} pages to the printer {1}".format(num, printer))
print("I just printed {num} pages to the printer {printer}".format(num=num, printer=printer))
print("I just printed %s pages to the printer %s" % (num, printer))
print("I just printed %(num)s pages to the printer %(printer)s" % {"num": num, "printer": printer})
Letterali stringa multilinea
Ci sono anche stringhe multilinea, che iniziano e finiscono con una serie di tre virgolette singole o doppie e funzionano come qui i documenti in Perl e Ruby .
Un semplice esempio con interpolazione variabile (usando il formatmetodo) è:
print("""Dear {recipient},
I wish you to leave Sunnydale and never return.
Not Quite Love,
{sender}
""".format(sender="Buffy the Vampire Slayer", recipient="Spike"))
corde grezze
Infine, tutti i tipi di stringa menzionati in precedenza sono disponibili in varietà " grezze " (indicate ponendo una r letterale prima della virgoletta di apertura), che non eseguono l'interpolazione con barre rovesciate e quindi sono molto utili per le espressioni regolari ; confronta "@-quoting" in C# . Le stringhe non elaborate sono state originariamente incluse specificamente per le espressioni regolari. A causa delle limitazioni del tokenizer, le stringhe non elaborate potrebbero non avere una barra rovesciata finale. La creazione di una stringa non elaborata contenente un percorso Windows che termina con una barra rovesciata richiede una serie di soluzioni alternative (in genere, l'utilizzo di barre anziché di barre rovesciate, poiché Windows accetta entrambe).
Esempi inclusi:
>>> # A Windows path, even raw strings cannot end in a backslash
>>> r"C:\Foo\Bar\Baz\"
File "<stdin>", line 1
r"C:\Foo\Bar\Baz\"
^
SyntaxError: EOL while scanning string literal
>>> dos_path = r"C:\Foo\Bar\Baz\ " # avoids the error by adding
>>> dos_path.rstrip() # and removing trailing space
'C:\\Foo\\Bar\\Baz\\'
>>> quoted_dos_path = r'"{}"'.format(dos_path)
>>> quoted_dos_path
'"C:\\Foo\\Bar\\Baz\\ "'
>>> # A regular expression matching a quoted string with possible backslash quoting
>>> re.match(r'"(([^"\\]|\\.)*)"', quoted_dos_path).group(1).rstrip()
'C:\\Foo\\Bar\\Baz\\'
>>> code = 'foo(2, bar)'
>>> # Reverse the arguments in a two-arg function call
>>> re.sub(r'\(([^,]*?),([^ ,]*?)\)', r'(\2, \1)', code)
'foo(2, bar)'
>>> # Note that this won't work if either argument has parens or commas in it.
Concatenazione di stringhe letterali adiacenti
Le stringhe letterali (utilizzando possibilmente convenzioni di virgolette diverse) che appaiono in modo contiguo e separate solo da spazi bianchi (comprese le nuove righe), sono consentite e vengono aggregate in una singola stringa più lunga. così
title = "One Good Turn: " \
'A Natural History of the Screwdriver and the Screw'
è equivalente a
title = "One Good Turn: A Natural History of the Screwdriver and the Screw"
Unicode
Da Python 3.0, il set di caratteri predefinito è UTF-8 sia per il codice sorgente che per l'interprete. In UTF-8, le stringhe unicode vengono gestite come le stringhe di byte tradizionali. Questo esempio funzionerà:
s = "Γειά" # Hello in Greek
print(s)
Numeri
I letterali numerici in Python sono del tipo normale, ad esempio 0, -1, 3.4, 3.5e-8.
Python ha interi di lunghezza arbitraria e aumenta automaticamente la loro dimensione di archiviazione secondo necessità. Prima di Python 3, c'erano due tipi di numeri integrali: i tradizionali interi di dimensione fissa e gli interi "lunghi" di dimensione arbitraria. La conversione in interi "lunghi" veniva eseguita automaticamente quando richiesto, e quindi il programmatore di solito non doveva essere a conoscenza dei due tipi integrali. Nelle versioni linguistiche più recenti la distinzione è completamente scomparsa e tutti gli interi si comportano come interi di lunghezza arbitraria.
Python supporta i normali numeri in virgola mobile , che vengono creati quando viene usato un punto in un letterale (es 1.1), quando un intero e un numero in virgola mobile vengono usati in un'espressione, o come risultato di alcune operazioni matematiche ("divisione vera" tramite l' /operatore, o l'elevamento a potenza con un esponente negativo).
Python supporta anche i numeri complessi in modo nativo. I numeri complessi sono indicati con il suffisso Jo j, ad es 3 + 4j.
Liste, tuple, set, dizionari
Python ha il supporto sintattico per la creazione di tipi di contenitori.
Le liste (class list) sono sequenze mutabili di elementi di tipo arbitrario, e possono essere create sia con la sintassi speciale
a_list = [1, 2, 3, "a dog"]
o usando la normale creazione di oggetti
a_second_list = list()
a_second_list.append(4)
a_second_list.append(5)
Le tuple (class tuple) sono sequenze immutabili di elementi di tipi arbitrari. C'è anche una sintassi speciale per creare tuple
a_tuple = 1, 2, 3, "four"
a_tuple = (1, 2, 3, "four")
Sebbene le tuple vengano create separando gli elementi con virgole, l'intero costrutto è solitamente racchiuso tra parentesi per aumentare la leggibilità. Una tupla vuota è indicata da ().
I set (class set) sono contenitori modificabili di elementi hashable di tipi arbitrari, senza duplicati. Gli elementi non sono ordinati, ma imposta l'iterazione di supporto sugli elementi. La sintassi per la creazione dell'insieme utilizza parentesi graffe
some_set = {0, (), False}
Gli insiemi Python sono molto simili agli insiemi matematici e supportano operazioni come l' intersezione e l' unione degli insiemi . Python dispone anche di una frozensetclasse per insiemi immutabili, vedere Tipi di raccolta .
I dizionari (class dict) sono mappature mutabili che legano chiavi e valori corrispondenti. Python ha una sintassi speciale per creare dizionari ( {key: value})
a_dictionary = {"key 1": "value 1", 2: 3, 4: []}
La sintassi del dizionario è simile alla sintassi impostata, la differenza è la presenza dei due punti. Il letterale vuoto {}risulta in un dizionario vuoto anziché in un set vuoto, che viene invece creato utilizzando il costruttore non letterale: set().
Operatori
Aritmetica
Python include gli operatori +, -, *, /("true division"), //( floor division), %( module ) e **( exponentiation ), con la loro consueta precedenza matematica .
In Python 3, x / yesegue la "divisione vera", il che significa che restituisce sempre un float, anche se entrambi xe ysono interi che si dividono in modo uniforme.
>>> 4 / 2
2.0
ed //esegue la divisione intera o la divisione floor , restituendo il floor del quoziente come intero.
In Python 2 (e nella maggior parte degli altri linguaggi di programmazione), a meno che non sia stato esplicitamente richiesto, ha x / yeseguito la divisione intera , restituendo un float solo se uno dei due input era un float. Tuttavia, poiché Python è un linguaggio tipizzato dinamicamente, non è sempre stato possibile dire quale operazione veniva eseguita, il che spesso portava a sottili bug, spingendo così all'introduzione //dell'operatore e al cambiamento nella semantica /dell'operatore in Python 3.
Operatori di confronto
Gli operatori di confronto, ad esempio ==, !=, <, >, <=, >=, is, is not, ine not invengono utilizzati su tutti i tipi di valori. Numeri, stringhe, sequenze e mappature possono essere confrontati. In Python 3, tipi disparati (come a stre an int) non hanno un ordinamento relativo coerente. Sebbene fosse possibile confrontare se una stringa fosse maggiore o minore di un numero intero in Python 2, questa era considerata una stranezza storica del design e alla fine fu rimossa in Python 3.
Espressioni di confronto concatenate come a < b < channo più o meno il significato che hanno in matematica, piuttosto che il significato insolito trovato in C e lingue simili. I termini vengono valutati e confrontati in ordine. L'operazione ha una semantica di cortocircuito , il che significa che la valutazione si interrompe non appena un verdetto è chiaro: se a < bè falso, cnon viene mai valutato poiché l'espressione non può più essere vera.
Per le espressioni senza effetti collaterali, a < b < cè equivalente a a < b and b < c. Tuttavia, c'è una differenza sostanziale quando le espressioni hanno effetti collaterali. a < f(x) < bvaluterà f(x)esattamente una volta, mentre a < f(x) and f(x) < blo valuterà due volte se il valore di aè minore di f(x)e una volta in caso contrario.
Operatori logici
In tutte le versioni di Python, operatori booleani trattano valori zero o valori vuoti come "", 0, None, 0.0, [], e {}come falsa, mentre nel trattamento generale non vuoti, non valori zero come vero. I valori booleani Truee Falsesono stati aggiunti al linguaggio in Python 2.2.1 come costanti (sottoclassi da 1e 0) e sono stati modificati per essere parole chiave complete in Python 3. Gli operatori di confronto binario come ==e >restituiscono Trueo False.
Gli operatori booleani ande orutilizzano la valutazione minima . Ad esempio, y == 0 or x/y > 100non solleverà mai un'eccezione di divisione per zero. Questi operatori restituiscono il valore dell'ultimo operando valutato, anziché Trueo False. Quindi l'espressione (4 and 5)restituisce 5, e (4 or 5)restituisce 4.
Programmazione funzionale
Come accennato in precedenza, un altro punto di forza di Python è la disponibilità di uno stile di programmazione funzionale . Come ci si può aspettare, questo rende il lavoro con elenchi e altre raccolte molto più semplice.
comprensioni
Una di queste costruzioni è la comprensione delle liste , che può essere espressa con il seguente formato:
L = [mapping_expression for element in source_list if filter_expression]
Utilizzo della comprensione delle liste per calcolare le prime cinque potenze di due:
powers_of_two = [2**n for n in range(1, 6)]
L' algoritmo Quicksort può essere espresso in modo elegante (anche se inefficiente) utilizzando la comprensione delle liste:
def qsort(L):
if L == []:
return []
pivot = L[0]
return (qsort([x for x in L[1:] if x < pivot]) +
[pivot] +
qsort([x for x in L[1:] if x >= pivot]))
Python 2.7+ supporta anche la comprensione degli insiemi e la comprensione del dizionario.
Funzioni di prima classe
In Python, le funzioni sono oggetti di prima classe che possono essere creati e passati in modo dinamico.
Il supporto limitato di Python per le funzioni anonime è il lambdacostrutto. Un esempio è la funzione anonima che eleva al quadrato il suo input, chiamata con l'argomento 5:
f = lambda x: x**2
f(5)
I lambda sono limitati a contenere un'espressione piuttosto che istruzioni , sebbene il flusso di controllo possa ancora essere implementato in modo meno elegante all'interno di lambda utilizzando il cortocircuito e in modo più idiomatico con le espressioni condizionali.
Chiusure
Python ha il supporto per le chiusure lessicali dalla versione 2.2. Ecco una funzione di esempio che restituisce una funzione che approssima la derivata della funzione data:
def derivative(f, dx):
"""Return a function that approximates the derivative of f
using an interval of dx, which should be appropriately small.
"""
def function(x):
return (f(x + dx) - f(x)) / dx
return function
La sintassi di Python, tuttavia, a volte porta i programmatori di altri linguaggi a pensare che le chiusure non siano supportate. L'ambito della variabile in Python è implicitamente determinato dall'ambito in cui si assegna un valore alla variabile, a meno che l'ambito non sia dichiarato esplicitamente con globalo nonlocal.
Si noti che il legame della chiusura di un nome a un valore non è modificabile dall'interno della funzione. Dato:
>>> def foo(a, b):
... print(f'a: {a}')
... print(f'b: {b}')
... def bar(c):
... b = c
... print(f'b*: {b}')
... bar(a)
... print(f'b: {b}')
...
>>> foo(1, 2)
a: 1
b: 2
b*: 1
b: 2
e si vede che b, come visibile dall'ambito della chiusura, mantiene il valore che aveva; il mutato legame ball'interno della funzione interna non si è propagato all'esterno. Il modo per aggirare questo è usare nonlocal bun'istruzione in bar. In Python 2 (che manca di nonlocal), la soluzione alternativa consiste nell'utilizzare un valore modificabile e modificare tale valore, non il legame. Ad esempio, una lista con un elemento.
Generatori
Introdotti in Python 2.2 come funzionalità facoltativa e finalizzati nella versione 2.3, i generatori sono il meccanismo di Python per la valutazione pigra di una funzione che altrimenti restituirebbe un elenco con spazio proibitivo o computazionalmente intensivo.
Questo è un esempio per generare pigramente i numeri primi:
from itertools import count
def generate_primes(stop_at=None):
primes = []
for n in count(start=2):
if stop_at is not None and n > stop_at:
return # raises the StopIteration exception
composite = False
for p in primes:
if not n % p:
composite = True
break
elif p ** 2 > n:
break
if not composite:
primes.append(n)
yield n
Quando si chiama questa funzione, il valore restituito può essere ripetuto in modo molto simile a un elenco:
for i in generate_primes(100): # iterate over the primes between 0 and 100
print(i)
for i in generate_primes(): # iterate over ALL primes indefinitely
print(i)
La definizione di un generatore sembra identica a quella di una funzione, tranne che la parola chiave yieldviene utilizzata al posto di return. Tuttavia, un generatore è un oggetto con stato persistente, che può entrare e uscire ripetutamente dallo stesso ambito. Una chiamata al generatore può quindi essere utilizzata al posto di un elenco o di un'altra struttura i cui elementi verranno iterati. Ogni volta che il forciclo nell'esempio richiede l'elemento successivo, viene chiamato il generatore e restituisce l'elemento successivo.
I generatori non devono essere infiniti come nell'esempio di numeri primi sopra. Quando un generatore termina, viene sollevata un'eccezione interna che indica a qualsiasi contesto chiamante che non ci sono più valori. forVerrà quindi terminato un ciclo o un'altra iterazione.
Espressioni generatore
Introdotte in Python 2.4, le espressioni del generatore sono l'equivalente pigro di valutazione delle comprensioni di lista. Utilizzando il generatore di numeri primi fornito nella sezione precedente, potremmo definire una raccolta pigra, ma non del tutto infinita.
from itertools import islice
primes_under_million = (i for i in generate_primes() if i < 1000000)
two_thousandth_prime = islice(primes_under_million, 1999, 2000).next()
La maggior parte della memoria e del tempo necessari per generare così tanti numeri primi non verranno utilizzati finché non si accederà effettivamente all'elemento necessario. Sfortunatamente, non puoi eseguire una semplice indicizzazione e slicing dei generatori, ma devi usare il modulo itertools o "roll your own" loop. Al contrario, una comprensione di elenco è funzionalmente equivalente, ma è avida nell'eseguire tutto il lavoro:
primes_under_million = [i for i in generate_primes(2000000) if i < 1000000]
two_thousandth_prime = primes_under_million[1999]
La comprensione dell'elenco creerà immediatamente un elenco ampio (con 78498 elementi, nell'esempio, ma creando transitoriamente un elenco di numeri primi sotto i due milioni), anche se alla maggior parte degli elementi non si accede mai. La comprensione del generatore è più parsimoniosa.
Dizionario e comprensioni degli insiemi
Mentre elenchi e generatori avevano comprensioni/espressioni, nelle versioni di Python precedenti alla 2.7 gli altri tipi di raccolta incorporati in Python (dict e set) dovevano essere inseriti nell'uso di elenchi o generatori:
>>> dict((n, n*n) for n in range(5))
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
Python 2.7 e 3.0 hanno unificato tutti i tipi di raccolta introducendo il dizionario e le comprensioni degli insiemi, simili alle comprensioni degli elenchi:
>>> [n*n for n in range(5)] # regular list comprehension
[0, 1, 4, 9, 16]
>>>
>>> {n*n for n in range(5)} # set comprehension
{0, 1, 4, 9, 16}
>>>
>>> {n: n*n for n in range(5)} # dict comprehension
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
oggetti
Python supporta la maggior parte delle tecniche di programmazione orientata agli oggetti (OOP). Consente il polimorfismo , non solo all'interno di una gerarchia di classi, ma anche mediante tipizzazione anatra . Qualsiasi oggetto può essere utilizzato per qualsiasi tipo e funzionerà purché disponga dei metodi e degli attributi appropriati. E tutto in Python è un oggetto, incluse classi, funzioni, numeri e moduli. Python supporta anche le metaclassi , uno strumento avanzato per migliorare le funzionalità delle classi. Naturalmente, è supportata l' ereditarietà , inclusa l'ereditarietà multipla . Python ha un supporto molto limitato per le variabili private che utilizzano il mangling dei nomi, che viene raramente utilizzato in pratica poiché l' occultamento delle informazioni è visto da alcuni come unpythonic , in quanto suggerisce che la classe in questione contiene interni antiestetici o mal pianificati. Lo slogan "qui siamo tutti utenti responsabili" è usato per descrivere questo atteggiamento.
Come è vero per i moduli, le classi in Python non pongono una barriera assoluta tra definizione e utente, ma si affidano piuttosto alla gentilezza dell'utente per non "irrompere nella definizione".
— 9. Classi , Tutorial Python 2.6 (2013)
Le dottrine OOP come l'uso di metodi di accesso per leggere i membri dei dati non vengono applicate in Python. Proprio come Python offre costrutti di programmazione funzionale ma non tenta di richiedere la trasparenza referenziale , offre un sistema di oggetti ma non richiede un comportamento OOP . Inoltre, è sempre possibile ridefinire la classe usando le proprietà (vedi Proprietà ) in modo che quando una certa variabile viene impostata o recuperata nel codice chiamante, invochi davvero una chiamata di funzione, in modo che spam.eggs = toastpossa davvero invocare spam.set_eggs(toast). Ciò annulla il vantaggio pratico delle funzioni di accesso e rimane OOP perché la proprietà eggsdiventa una parte legittima dell'interfaccia dell'oggetto: non è necessario che rifletta un dettaglio di implementazione.
Nella versione 2.2 di Python sono state introdotte le classi "nuovo stile". Con classi di nuovo stile, oggetti e tipi sono stati unificati, consentendo la sottoclasse dei tipi. È possibile definire anche tipi completamente nuovi, completi di comportamenti personalizzati per gli operatori infissi. Ciò consente di eseguire molte cose radicali sintatticamente all'interno di Python. Con Python 2.3 è stato adottato anche un nuovo ordine di risoluzione dei metodi per l'ereditarietà multipla. È anche possibile eseguire codice personalizzato durante l'accesso o l'impostazione degli attributi, sebbene i dettagli di tali tecniche si siano evoluti tra le versioni di Python.
Con dichiarazione
L' withistruzione gestisce le risorse e consente agli utenti di lavorare con il protocollo Context Manager. Una funzione ( __enter__()) viene chiamata quando si entra nell'ambito e un'altra ( __exit__()) quando si esce. Ciò impedisce di dimenticare di liberare la risorsa e gestisce anche situazioni più complicate come liberare la risorsa quando si verifica un'eccezione mentre è in uso. I gestori di contesto vengono spesso utilizzati con file, connessioni a database, casi di test, ecc.
Proprietà
Le proprietà consentono di richiamare metodi appositamente definiti su un'istanza di oggetto utilizzando la stessa sintassi utilizzata per l'accesso agli attributi. Un esempio di una classe che definisce alcune proprietà è:
class MyClass:
def __init__(self):
self._a = None
@property
def a(self):
return self._a
@a.setter # makes the property writable
def a(self, value):
self._a = value
descrittori
Una classe che definisce uno o più dei tre metodi speciali __get__(self, instance, owner), __set__(self, instance, value), __delete__(self, instance)può essere usata come descrittore. La creazione di un'istanza di un descrittore come membro di una classe di una seconda classe rende l'istanza una proprietà della seconda classe.
Metodi di classe e statici
Python consente la creazione di metodi di classe e metodi statici tramite l'uso dei decoratori@classmethod e . Il primo argomento di un metodo di classe è l'oggetto classe invece dell'autoriferimento all'istanza. Un metodo statico non ha un primo argomento speciale. Né l'istanza né l'oggetto classe vengono passati a un metodo statico.
@staticmethod
Eccezioni
Python supporta (e utilizza ampiamente) la gestione delle eccezioni come mezzo per testare condizioni di errore e altri eventi "eccezionali" in un programma. In effetti, è persino possibile intercettare l'eccezione causata da un errore di sintassi .
Lo stile Python richiede l'uso di eccezioni ogni volta che potrebbe verificarsi una condizione di errore. Piuttosto che testare l'accesso a un file o una risorsa prima di utilizzarlo effettivamente, è normale in Python andare avanti e provare a usarlo, catturando l'eccezione se l'accesso viene rifiutato.
Le eccezioni possono essere utilizzate anche come mezzo più generale di trasferimento non locale del controllo, anche quando non si tratta di un errore. Ad esempio, il software della mailing list Mailman , scritto in Python, utilizza le eccezioni per saltare fuori dalla logica di gestione dei messaggi profondamente nidificata quando è stata presa la decisione di rifiutare un messaggio o tenerlo per l'approvazione del moderatore.
Le eccezioni sono spesso usate come alternativa al if-block, specialmente in situazioni con thread . Un motto comunemente invocato è EAFP, o "È più facile chiedere perdono che permesso", che è attribuito a Grace Hopper . L'alternativa, nota come LBYL, o "Look Before You Leap", verifica esplicitamente le pre-condizioni.
In questo primo esempio di codice, seguendo l'approccio LBYL, c'è un controllo esplicito per l'attributo prima dell'accesso:
if hasattr(spam, 'eggs'):
ham = spam.eggs
else:
handle_missing_attr()
Questo secondo esempio segue il paradigma EAFP:
try:
ham = spam.eggs
except AttributeError:
handle_missing_attr()
Questi due esempi di codice hanno lo stesso effetto, anche se ci saranno differenze di prestazioni. Quando spamha l'attributo eggs, l'esempio EAFP verrà eseguito più velocemente. Quando spamnon ha l'attributo eggs(il caso "eccezionale"), l'esempio EAFP verrà eseguito più lentamente. Il profiler Python può essere utilizzato in casi specifici per determinare le caratteristiche delle prestazioni. Se i casi eccezionali sono rari, la versione EAFP avrà prestazioni medie superiori rispetto all'alternativa. Inoltre, evita l'intera classe di vulnerabilità time-of-check-to-time-of-use (TOCTTOU), altre condizioni di gara ed è compatibile con la tipizzazione dell'anatra . Uno svantaggio di EAFP è che può essere utilizzato solo con istruzioni; un'eccezione non può essere rilevata in un'espressione del generatore, una comprensione di elenchi o una funzione lambda.
Commenti e docstring
Python ha due modi per annotare il codice Python. Uno è usare i commenti per indicare cosa fa una parte del codice. I commenti a riga singola iniziano con il carattere cancelletto ( #) e continuano fino alla fine della riga. I commenti che si estendono su più di una riga si ottengono inserendo una stringa multilinea (con """o '''come delimitatore su ciascuna estremità) che non viene utilizzata nell'assegnazione o altrimenti valutata, ma si trova tra altre istruzioni.
Commentando un pezzo di codice:
import sys
def getline():
return sys.stdin.readline() # Get one line and return it
Commentare un pezzo di codice con più righe:
def getline():
return sys.stdin.readline() """this function
gets one line
and returns it"""
Docstrings (stringhe di documentazione), ovvero stringhe che si trovano da sole senza assegnazione come prima riga rientrata all'interno di un modulo, classe, metodo o funzione, impostano automaticamente il loro contenuto come un attributo denominato __doc__, che ha lo scopo di memorizzare una descrizione leggibile dall'uomo dello scopo, del comportamento e dell'uso dell'oggetto. La helpfunzione incorporata genera il suo output in base agli __doc__attributi. Tali stringhe possono essere delimitate con "o 'per stringhe a riga singola, o possono estendersi su più righe se delimitate con una delle due """o '''che è la notazione di Python per specificare le stringhe su più righe. Tuttavia, la guida di stile per la lingua specifica che le virgolette doppie triple ( """) sono preferite sia per le docstring a riga singola che per quelle multilinea.
Stringa di documentazione a riga singola:
def getline():
"""Get one line from stdin and return it."""
return sys.stdin.readline()
Stringa di documentazione su più righe:
def getline():
"""Get one line
from stdin
and return it.
"""
return sys.stdin.readline()
Le stringhe di documentazione possono essere grandi quanto il programmatore desidera e contenere interruzioni di riga . In contrasto con i commenti, le docstring sono esse stesse oggetti Python e fanno parte del codice interpretato che Python esegue. Ciò significa che un programma in esecuzione può recuperare le proprie docstring e manipolare tali informazioni, ma l'uso normale è quello di fornire ad altri programmatori informazioni su come richiamare l'oggetto documentato nella docstring.
Sono disponibili strumenti in grado di estrarre le docstring dal codice Python e generare documentazione. È possibile accedere alla documentazione di Docstring anche dall'interprete con la help()funzione o dalla shell con il comando pydocpydoc .
Il modulo standard doctest utilizza le interazioni copiate dalle sessioni della shell Python in docstring per creare test, mentre il modulo docopt le utilizza per definire le opzioni della riga di comando.
Annotazioni sulle funzioni
Le annotazioni di funzione (suggerimenti di tipo) sono definite in PEP 3107. Consentono di allegare dati agli argomenti e di restituire una funzione. Il comportamento delle annotazioni non è definito dal linguaggio ed è lasciato a framework di terze parti. Ad esempio, una libreria potrebbe essere scritta per gestire la digitazione statica:
def haul(item: Haulable, *vargs: PackAnimal) -> Distance
Decoratori
Un decoratore è qualsiasi oggetto Python richiamabile utilizzato per modificare una funzione, un metodo o una definizione di classe. A un decoratore viene passato l'oggetto originale in fase di definizione e restituisce un oggetto modificato, che viene quindi associato al nome nella definizione. I decoratori Python sono stati ispirati in parte dalle annotazioni Java e hanno una sintassi simile; la sintassi del decoratore è puro zucchero sintattico , utilizzando @come parola chiave:
@viking_chorus
def menu_item():
print("spam")
è equivalente a
def menu_item():
print("spam")
menu_item = viking_chorus(menu_item)
I decoratori sono una forma di metaprogrammazione ; migliorano l'azione della funzione o del metodo che decorano. Ad esempio, nell'esempio seguente, viking_choruspotrebbe causare menu_iteml'esecuzione 8 volte (vedi schizzo Spam ) per ogni volta che viene chiamato:
def viking_chorus(myfunc):
def inner_func(*args, **kwargs):
for i in range(8):
myfunc(*args, **kwargs)
return inner_func
Gli usi canonici dei decoratori di funzioni sono per creare metodi di classe o metodi statici , aggiungere attributi di funzione, tracciare , impostare pre e postcondizioni e sincronizzazione , ma possono essere utilizzati per molto altro, inclusa l' eliminazione della ricorsione in coda , la memoizzazione e persino il miglioramento della scrittura di altri decoratori.
I decoratori possono essere concatenati posizionandone diversi su linee adiacenti:
@invincible
@favourite_colour("Blue")
def black_knight():
pass
è equivalente a
def black_knight():
pass
black_knight = invincible(favourite_colour("Blue")(black_knight))
oppure, usando variabili intermedie
def black_knight():
pass
blue_decorator = favourite_colour("Blue")
decorated_by_blue = blue_decorator(black_knight)
black_knight = invincible(decorated_by_blue)
Nell'esempio sopra, la fabbrica difavourite_colour decoratori accetta un argomento. Le fabbriche di decoratori devono restituire un decoratore, che viene quindi chiamato con l'oggetto da decorare come argomento:
def favourite_colour(colour):
def decorator(func):
def wrapper():
print(colour)
func()
return wrapper
return decorator
Ciò decorerebbe quindi la black_knightfunzione in modo tale che il colore, "Blue", venga stampato prima black_knightdell'esecuzione della funzione. La chiusura assicura che l'argomento color sia accessibile alla funzione wrapper più interna anche quando viene restituito ed esce dall'ambito, che è ciò che consente ai decoratori di funzionare.
Nonostante il nome, i decoratori Python non sono un'implementazione del pattern decoratore . Il modello decoratore è un modello di progettazione utilizzato nei linguaggi di programmazione orientati agli oggetti tipizzati staticamente per consentire l'aggiunta di funzionalità agli oggetti in fase di esecuzione; I decoratori Python aggiungono funzionalità a funzioni e metodi in fase di definizione e quindi sono un costrutto di livello superiore rispetto alle classi di pattern decoratori. Il pattern del decoratore stesso è banalmente implementabile in Python, perché il linguaggio è di tipo duck e quindi di solito non è considerato come tale.
uova di Pasqua
Gli utenti di linguaggi con parentesi graffe , come C o Java , a volte si aspettano o desiderano che Python segua una convenzione di delimitatore di blocchi. La sintassi del blocco delimitata da parentesi graffe è stata ripetutamente richiesta e costantemente rifiutata dagli sviluppatori principali. L'interprete Python contiene un easter egg che riassume i sentimenti dei suoi sviluppatori su questo problema. Il codice from __future__ import bracessolleva l'eccezione SyntaxError: not a chance. Il __future__modulo viene normalmente utilizzato per fornire funzionalità dalle versioni future di Python.
Un altro messaggio nascosto, lo Zen di Python (un riassunto della filosofia di progettazione di Python ), viene visualizzato quando si tenta di import this.
Il messaggio Hello world!viene stampato quando import __hello__viene utilizzata l'istruzione di importazione . In Python 2.7, invece di Hello world!stampare Hello world....
L'importazione del antigravitymodulo apre un browser Web in xkcd comic 353 che ritrae un uso umoristico immaginario di tale modulo, inteso a dimostrare la facilità con cui i moduli Python consentono funzionalità aggiuntive. In Python 3, questo modulo contiene anche un'implementazione dell'algoritmo "geohash", un riferimento a xkcd comic 426 .
Riferimenti
link esterno
- Tutorial Python scritto dall'autore di Python, Guido van Rossum.