[Python] Ottimizzazione codice

Daniele Zambelli daniele.zambelli a inwind.it
Dom 27 Apr 2008 20:31:15 CEST


Alle domenica 27 aprile 2008, Leonardo ha scritto:
> Salve a tutti.
> Sono agli inizi con Python, sto imparando grazie a varie guide trovate
> online, e sto iniziando ad addentrarmi nell'ottimizzazione del codice.

Benvenuto

> Ora, ho scritto questo piccolo programmino (con GUI in pyGTK) che
> accetta dei numeri come input, ne calcola la media, e restituisce la
> media, il numero maggiore immesso, il numero minore, il numero più
> vicino alla media.

Sei partito da un problema classico che dà diversi spunti di riflessione. È 
interessante la combinazione tra l'interfaccia grafica e gli algoritmi di 
elaborazione dati.

> Ora, vorrei dei consigli su come ottimizzare il codice, magari
> sostituendo qualche ciclo if con qualcosa di più performante o simili =D
> posto il codice: (non insultatemi perchè non c'è nemmeno un commento o
> una doc string xD)

Qualche commento ci sta bene, di solito, ma hai usato nomi delle funzioni 
sufficientemente esplicativi e quindi il tuo codice è chiaro.

Non ho le gtk e non ho il tempo di provare il codice che posto, per cui prendi 
le cose che scrivo come degli spunti per approfondire.
Altro avvertimento, in lista ci sono molti programmatori più competenti di 
me... fa la tara a quello che scrivo.

1. ottimizzazione: Di solito è buona norma preoccuparsi delle performances di 
un programa più avanti... comunque ridurre le righe di codice va nella 
direzione di rendere il programma più semplice e di solito più efficiente.

>
>
>         import pygtk
>         pygtk.require('2.0')
>         import gtk
>
>         def Media(numeri):
>         	divisore = len(numeri)
>         	a = 0
>         	for num in numeri:
>         		a = a + float(num)
>         	try:
>         		media = float(a) / float(divisore)
>         		return media
>         	except:
>         		return None

2. "a" lo chiamerei somma, numeri contiene già dei numeri, non c'è motivo per 
cui la divisione non possa funzionare a ameno che la lista sia vuota.
Quindi la funzione potrebbe diventare:

>         def Media(numeri):
>         	if not numeri:
>         	        return None
>         	somma = 0
>         	for num in numeri:
>         		somma += num
>         	return float(somma) / len(numeri)

tenendo presente che Python è in grado di sommare gli elementi di una lista la 
funzione si può semplificare ulteriormente usando la funzione sum(lista).

>         def DistanzaDallaMedia(numero, media):
>         	numero = float(numero)
>         	distanzamedia = abs(numero - media)
>         	return distanzamedia

3. Anche qui le cose si possono semplificare:

>         def DistanzaDallaMedia(numero, media):
>         	return abs(numero - media)

>         class Finestra:
>         	def __init__(self):
>         		self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
>         		self.win.set_title("Calcolo media aritmetica")
>         		self.win.connect("destroy", self.esci)
>         		self.win.connect("delete_event", self.delete_event)
>         		self.win.set_default_size(400,300)
>         		self.vbox = gtk.VBox(0,0)
>         		self.label = gtk.Label("Inserisci qui sotto i numeri dei quali
>         vuoi fare la media, separati da spazi.")
>         		self.entry = gtk.Entry(100)
>         		self.button = gtk.Button("Esegui", gtk.STOCK_EXECUTE)
>         		self.button.connect("clicked", self.CalcolaMedia)
>         		self.risultato = gtk.Label("Clicca su esegui, qui comparira'
>         il risultato")
>         		self.statistiche = gtk.Label("Clicca su esegui, qui
>         compariranno alcune statistiche")
>         		self.vbox.pack_start(self.label)
>         		self.vbox.pack_start(self.entry)
>         		self.vbox.pack_start(self.button)
>         		self.vbox.pack_start(self.risultato)
>         		self.vbox.pack_end(self.statistiche)
>         		self.win.add(self.vbox)
>         		self.win.show_all()
>
>         	def GetUserInput(self, widget):
>         		UserInput = widget.get_text()
>         		return UserInput
>
>         	def OttieniNumeri(self):
>         		UserInput = self.GetUserInput(self.entry)
>         		import string
>         		Numeri = string.split(UserInput)
>         		Numeri = map(int, Numeri)
>         		return Numeri

4. Per la maggior parte dei compiti non è necessario importare string, 
utilizzando le funzioni in cascata si possono evitare le variabili:

>         	def OttieniNumeri(self):
>         		return map(int, self.GetUserInput(self.entry).split())


>         	def CalcolaMedia(self, widget):
>         		Numeri = self.OttieniNumeri()
>         		MediaCalcolata = Media(Numeri)
>         		if MediaCalcolata:
>         			self.risultato.set_text("La media calcolata equivale a  " +
>         str(MediaCalcolata))
>         			Statistiche = {}
>         			for num in Numeri:
>         				if 'maggiore' not in Statistiche.keys():
>         					Statistiche['maggiore'] = num
>         					Statistiche['minore'] = num
>         					Statistiche['vicino_media'] = num
>         				else:
>         					if DistanzaDallaMedia(num, MediaCalcolata) <
>         DistanzaDallaMedia(Statistiche['vicino_media'], MediaCalcolata):
>         						Statistiche['vicino_media'] = num
>         					if num > Statistiche['maggiore']:
>         						Statistiche['maggiore'] = num
>         					elif num < Statistiche['minore']:
>         						Statistiche['minore'] = num
>         			self.StampaStatistiche(Statistiche)
>         		else:
>         			self.risultato.set_text("Devi inserire almeno un numero!")

5. Questa funzione è un po' compliacata.
5.1. Io cambierei il nome...
5.2. Nel comando: self.risultato.set_text(...) userei il costruttore di 
stringhe:"La media calcolata equivale a %s " % MediaCalcolata
5.3. Nel ciclo per calcolare i massimi e i minimi toglierei l'if 
inizializzando il dizionario all'esterno del ciclo:
num=numeri[0]
statistiche={'maggiore': num, 'minore': num, 'vicino_media': num}
for num in numeri[1:]:
    ....
5.4. Io inserirei questo ciclo in una funzione a parte in modo che la funzione 
CalcolaMedia non abbia cicli al suo interno.


>         	def StampaStatistiche(self, Statistiche):
>         		self.statistiche.set_text("Il numero maggiore inseirto e' " +
>         str(Statistiche['maggiore']) + "\nIl numero minore inserito e' "
>         + str(Statistiche['minore']) + "\nIl numero inserito piu' vicino
>         alla media e': " + str(Statistiche['vicino_media']))

>         	def delete_event(self, widget, event, data=None):
>         		return False
>
>         	def esci(self, widget, data=None):
>         		gtk.main_quit()
>
>         	def main(self):
>         		gtk.main()
>
>         if __name__ == "__main__":
>         	PrimaFinestra = Finestra()
>         	PrimaFinestra.main()

6. Alcune osservazioni varie:
6.1. I nomi delle variabili e delle funzioni, di solito vengono scritti tutti 
in minuscolo, la prima lettera maiuscola è riservata per le classi e tutto 
maiuscolo per le "costanti".
6.2. Io preferisco indentazioni minori in modo da non avere righe molto 
lunghe, 4 caratteri dovrebbero bastare.
6.3. Non ho provato gli spezzoni di codice che ho scritto, vedi tu se 
funziona...

Ciao e buon divertimento con Python.

-- 

Daniele                        www.fugamatematica.blogspot.com

                          giusto!
  _o)  (o_       (o_ _o)  nel verso
-./\\  //\.-     //\ /\\  forse è perché non guardiamo le cose
 _\_V  V_/_      V_/ \_V  Quando non ci capiamo,


Maggiori informazioni sulla lista Python