Logo Zephyrnet

5 trucchi comuni di Python (e come evitarli) – KDnuggets

Data:

5 trucchi comuni di Python (e come evitarli)
Immagine dell'autore
 

Python è un linguaggio di programmazione versatile e adatto ai principianti noto per la sua semplicità e leggibilità. La sua sintassi elegante, tuttavia, non è immune da stranezze che possono sorprendere anche gli sviluppatori Python esperti. E comprenderli è essenziale per scrivere codice privo di bug o, se preferisci, eseguire un debug senza problemi.

Questo tutorial esplora alcuni di questi trucchi: impostazioni predefinite modificabili, ambito variabile in loop e comprensioni, assegnazione di tuple e altro ancora. Codificheremo semplici esempi da vedere perché le cose funzionano come funzionano e anche guardate come possiamo evitarli (se davvero possiamo 🙂). 

Allora cominciamo!

In Python, i valori predefiniti modificabili sono comuni spigoli vivi. Ti imbatterai in un comportamento inaspettato ogni volta che definisci una funzione con oggetti mutabili, come elenchi o dizionari, come argomenti predefiniti. 

Il valore predefinito viene valutato solo una volta, quando la funzione viene definita, e non ogni volta che la funzione viene chiamata. Ciò può portare a comportamenti imprevisti se si modifica l'argomento predefinito all'interno della funzione.

Facciamo un esempio:

def add_to_cart(item, cart=[]):
    cart.append(item)
    return cart

 

In questo esempio, add_to_cart è una funzione che prende un elemento e lo aggiunge a un elenco cart. Il valore predefinito di cart è un elenco vuoto. Significa che chiamare la funzione senza un articolo da aggiungere restituisce un carrello vuoto. 

Ed ecco un paio di chiamate di funzione:

# User 1 adds items to their cart
user1_cart = add_to_cart("Apple")
print("User 1 Cart:", user1_cart)  

 

Output >>> ['Apple']

 

Funziona come previsto. Ma cosa succede adesso?

# User 2 adds items to their cart
user2_cart = add_to_cart("Cookies")
print("User 2 Cart:", user2_cart) 

 

Output >>>

['Apple', 'Cookies'] # User 2 never added apples to their cart!

 

Poiché l'argomento predefinito è una lista, un oggetto mutabile, mantiene il suo stato tra le chiamate alla funzione. Quindi ogni volta che chiami add_to_cart, aggiunge il valore allo stesso oggetto elenco creato durante la definizione della funzione. In questo esempio, è come se tutti gli utenti condividessero lo stesso carrello.

Come evitare

Come soluzione alternativa, è possibile impostare cart a None e inizializzare il carrello all'interno della funzione in questo modo:

def add_to_cart(item, cart=None):
    if cart is None:
        cart = []
    cart.append(item)
    return cart

 

Quindi ogni utente ora ha un carrello separato. 🙂

Se hai bisogno di un aggiornamento sulle funzioni Python e sugli argomenti delle funzioni, leggi Argomenti delle funzioni Python: una guida definitiva.

Le stranezze dell'ambito di Python richiedono un tutorial tutto loro. Ma qui esamineremo una di queste stranezze.

Guarda il seguente frammento:

x = 10
squares = []
for x in range(5):
    squares.append(x ** 2)

print("Squares list:", squares)  

# x is accessible here and is the last value of the looping var
print("x after for loop:", x)

 

La variabile x è impostato su 10. Ma x è anche la variabile di loop. Ma supponiamo che l'ambito della variabile di loop sia limitato al blocco del ciclo for, giusto?

Diamo un'occhiata all'output:

Output >>>

Squares list: [0, 1, 4, 9, 16]
x after for loop: 4

 

Lo vediamo x ora è 4, il valore finale che assume nel ciclo, e non il valore iniziale di 10 su cui lo abbiamo impostato.

Ora vediamo cosa succede se sostituiamo il ciclo for con un'espressione di comprensione:

x = 10
squares = [x ** 2 for x in range(5)]

print("Squares list:", squares)  

# x is 10 here
print("x after list comprehension:", x)

 

Qui, x è 10, il valore a cui lo impostiamo prima dell'espressione di comprensione:

Output >>>

Squares list: [0, 1, 4, 9, 16]
x after list comprehension: 10

Come evitare

Per evitare comportamenti imprevisti: se utilizzi i cicli, assicurati di non denominare la variabile del ciclo come un'altra variabile a cui desideri accedere in seguito.

In Python usiamo il is parola chiave per verificare l'identità dell'oggetto. Significa che controlla se due variabili fanno riferimento allo stesso oggetto in memoria. E per verificare l'uguaglianza, usiamo il == operatore. SÌ?

Ora avvia un REPL Python ed esegui il seguente codice:

>>> a = 7
>>> b = 7
>>> a == 7
True
>>> a is b
True

 

Ora esegui questo:

>>> x = 280
>>> y = 280
>>> x == y
True
>>> x is y
False

 

Aspetta, perché succede questo? Ebbene, ciò è dovuto al “caching degli interi” o all’“internamento” in CPython, l’implementazione standard di Python.

Python memorizza nella cache oggetti interi nell'intervallo da -5 a 256. Ciò significa che ogni volta che usi un numero intero all'interno di questo intervallo, Python utilizzerà lo stesso oggetto in memoria. Pertanto, quando si confrontano due numeri interi all'interno di questo intervallo utilizzando il metodo is parola chiave, il risultato è True perché fare riferimento allo stesso oggetto in memoria.

Ecco perché a is b problemi True. Puoi verificarlo anche stampandolo id(a) ed id(b).

Tuttavia, i numeri interi esterni a questo intervallo non vengono memorizzati nella cache. E ogni occorrenza di tali numeri interi crea un nuovo oggetto in memoria. 

Pertanto, quando confronti due numeri interi al di fuori dell'intervallo memorizzato nella cache utilizzando il metodo is parola chiave (sì, x ed y entrambi impostati su 280 nel nostro esempio), il risultato è False perché sono davvero due oggetti diversi nella memoria.

Come evitare

Questo comportamento non dovrebbe rappresentare un problema a meno che non si provi a utilizzare il file is per confrontare l'uguaglianza di due oggetti. Quindi usa sempre il == operatore per verificare se due oggetti Python hanno lo stesso valore.

Se hai familiarità con le strutture dati integrate in Python, sai che le tuple lo sono immutabile. Quindi tu non può modificarli sul posto. Le strutture dati come elenchi e dizionari, invece, lo sono mutevole. Cioè te può cambiarli sul posto.

Ma che dire delle tuple che contengono uno o più oggetti mutabili?

È utile avviare un REPL Python ed eseguire questo semplice esempio:

>>> my_tuple = ([1,2],3,4)
>>> my_tuple[0].append(3)
>>> my_tuple
([1, 2, 3], 3, 4)

 

Qui, il primo elemento della tupla è una lista con due elementi. Proviamo ad aggiungerne 3 al primo elenco e funziona bene! Bene, abbiamo appena modificato una tupla sul posto?

Ora proviamo ad aggiungere altri due elementi alla lista, ma questa volta utilizzando l'operatore +=:

>>> my_tuple[0] += [4,5]
Traceback (most recent call last):
  File "", line 1, in 
TypeError: 'tuple' object does not support item assignment

 

Sì, ottieni un TypeError che dice che l'oggetto tupla non supporta l'assegnazione di elementi. Ciò che è previsto. Ma controlliamo la tupla: 

>>> my_tuple
([1, 2, 3, 4, 5], 3, 4)

 

Vediamo che gli elementi 4 e 5 sono stati aggiunti alla lista! Il programma ha appena generato un errore e allo stesso tempo ha avuto successo?

Bene, l'operatore += funziona internamente chiamando il metodo __iadd__() metodo che esegue l'addizione sul posto e modifica l'elenco sul posto. L'assegnazione solleva un'eccezione TypeError, ma l'aggiunta di elementi alla fine dell'elenco è già riuscita. += è forse l'angolo più acuto!

Come evitare

Per evitare tali problemi nel tuo programma, prova a utilizzare le tuple esclusivamente per le collezioni immutabili. Ed evitare il più possibile di utilizzare oggetti mutabili come elementi tupla.

La mutabilità è stata finora un argomento ricorrente nella nostra discussione. Quindi eccone un altro per concludere questo tutorial.

A volte potrebbe essere necessario creare copie indipendenti degli elenchi. Ma cosa succede quando crei una copia utilizzando una sintassi simile a list2 = list1 where list1 è l'elenco originale?

È una copia superficiale quella che viene creata. Quindi copia solo i riferimenti agli elementi originali dell'elenco. La modifica degli elementi tramite la copia superficiale avrà effetto entrambi l'elenco originale ed la copia superficiale. 

Prendiamo questo esempio:

original_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# Shallow copy of the original list
shallow_copy = original_list

# Modify the shallow copy
shallow_copy[0][0] = 100

# Print both the lists
print("Original List:", original_list)
print("Shallow Copy:", shallow_copy)

 

Vediamo che le modifiche alla copia superficiale influiscono anche sull'elenco originale:

Output >>>

Original List: [[100, 2, 3], [4, 5, 6], [7, 8, 9]]
Shallow Copy: [[100, 2, 3], [4, 5, 6], [7, 8, 9]]

 

Qui modifichiamo il primo elemento della prima lista nidificata nella copia superficiale: shallow_copy[0][0] = 100. Ma vediamo che la modifica riguarda sia l'elenco originale che la copia superficiale. 

Come evitare

Per evitare ciò, puoi creare una copia profonda in questo modo:

import copy

original_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# Deep copy of the original list
deep_copy = copy.deepcopy(original_list)

# Modify an element of the deep copy
deep_copy[0][0] = 100

# Print both lists
print("Original List:", original_list)
print("Deep Copy:", deep_copy)

 

Ora, qualsiasi modifica alla copia profonda lascia invariato l'elenco originale.

Output >>>

Original List: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Deep Copy: [[100, 2, 3], [4, 5, 6], [7, 8, 9]]

E questo è tutto! In questo tutorial, abbiamo esplorato diverse stranezze di Python: dal comportamento sorprendente dei valori predefiniti modificabili alle sottigliezze delle liste di copia superficiali. Questa è solo un'introduzione alle stranezze di Python e non è affatto un elenco esaustivo. Puoi trovare tutti gli esempi di codice su GitHub.

Man mano che continui a programmare più a lungo in Python e a comprendere meglio il linguaggio, forse ti imbatterai in molti altri di questi. Quindi, continua a programmare, continua a esplorare!

Oh, e facci sapere nei commenti se desideri leggere un seguito di questo tutorial.
 
 

Bala Priya C è uno sviluppatore e scrittore tecnico indiano. Le piace lavorare all'intersezione tra matematica, programmazione, scienza dei dati e creazione di contenuti. Le sue aree di interesse e competenza includono DevOps, scienza dei dati ed elaborazione del linguaggio naturale. Le piace leggere, scrivere, programmare e bere caffè! Attualmente sta lavorando per apprendere e condividere le sue conoscenze con la comunità degli sviluppatori creando tutorial, guide pratiche, articoli di opinione e altro ancora. Bala crea anche panoramiche accattivanti delle risorse ed esercitazioni sulla codifica.

spot_img

L'ultima intelligenza

spot_img