[Python] variabili globali e d'istanza: provengo da java
enrico franchi
enrico.franchi a gmail.com
Mer 26 Dic 2007 12:39:38 CET
On Dec 26, 2007 11:47 AM, ugaciaka <ugaciaka a gmail.com> wrote:
> Riassumendo: ho scritto un programmino (senza nessuna classe ma con
> sole funzioni), ho notato che se passo da paramentro una variabile per
> fargli capire al compilatore che non è una variabili d'istanza (cioé
> della funzione) ma di tutto del corpo del programma devo anteporre
> global al nome della variabile passata.
Allora, affrontiamo la cosa su due piani distinti. Primo piano:
1. evitare le variabili globali quanto più possibile. tu non hai
bisogno di usare variabili globali. *NON* usarle.
2. veniamo al significato del concetto di global (lo statement, intendo).
Come ti sarai reso conto in Python le dichiarazioni *non* esistono. Le
variabili quando le vuoi, le usi. Le variabili tipicamente hanno due
stati 'bound' e 'unbound'. Dopo un assegnamento una variabile è bound
all'oggetto che le è stato assegnato. Se semplicemente uso una
variabile unbound (un nome 'vuoto') per esempio chiamando dei metodi,
ho degli errori.
A questo punto veniamo al concetto di global.
def foo(i):
return a * i
a = 5
print foo(2)
Stampa chiaramente 10. Quando va a cercare la variabile 'a' la trova
(nello scope globale) e usa quella.
Il problema è invece quando io ho un assegnamento. Consideriamo questo
frammento:
def foo(i):
return a * i
def goo():
a = 3
return a
a = 5
print foo(2)
goo()
print foo(2)
Immagina che casino sarebbe se goo senza dire niente a nessuno
*modificasse* una variabile globale solo perchè ne trova una. La
chiamata della funzione non sarebbe più indipendente dal contesto (il
caso di foo è diverso, è vero che è dipendente dal contesto pure lei,
ma non modifica proditoriamente lo stato del programma, al limite
fallisce se chiamata in uno scope inadatto, ma questo è un problema
comune a tutte le funzioni che usano variabili globali, per inciso se
goo andasse a modificare a, si avrebbero alcuni dei problemi presenti
con i linguaggi che usano lo scope dinamico).
Bene: a questo punto entra in gioco global. Se io dentro goo scrivo
'global a' sto dicendo *esplicitamente* che non mi deve fare il
binding di una variabile locale chiamata a con l'oggetto 3, ma deve
fare il binding della variabile globale a con l'oggetto 3 (si, il
numero 3 è un oggetto).
> Ok fin qua ci sono. Ma se io volessi ora mettere tutto in una classe
> come agisco? Dove definisco le variabili globali? Nella classe?
> All'interno del costruttore?...
Scusa, ma io qui intravedo notevole confusione (al di la di Python o
Java). Una variabile globale è globale, non è in un oggetto. Se no
parliamo di variabili membro, campi, variabili d'istanza, whatever,
oppure variabili di classe (se sono appunto legate alla classe e non
alle istanze).
> Il mio programmino di esercizio è questo sotto e praticamente non fa
> altro che sostituire il carattere "_" con uno spazio " ".
Ok, mi permetto di indirizzarti un pochetto sulla buona strada.
1. la nomenclatura delle funzioni è atroce. è faticosa da leggere e
poco elegante
2. no globali please (specie in quanto risolvi il problema aggiungendo
semplicemente un parametro per funzione, mica 54)
3. la funzione 'rinomina file' è in buona sostanza inutile. ha
praticamente la stessa complessità dello statement che contiene, non
astrae molto e non semplifica. questo è comunque opinabile. io
personalmente preferisco leggere replace("_", " ") che andare a
scoprire cosa si intende con 'rinomina'.
4. i cicli for sono allucinanti e poco efficienti. In Python puoi usare l'idioma
for item in lista:
# do something with item
Gli inserimenti in mezzo alla lista sono molto meno efficienti degli
inserimenti in coda.
5. perchè passare l'albero? non è meglio lasciare tutta la logica
dentro il metodo?
> #COSA INVECE VORREI FARE
> #io vorrei invece fare solo, dopo aver creato la classe che ingloba i
> miei metodi:
> #oggetto=Classe(carattere_da_sostituire, carattere_sostituito)
> #oggetto.rinomina_file()
Qualcosa tipo questo? (non è testato, se noti le chiamate di sistema
sono commentate, prova pure)
import os
from os.path import join
class TermUI(object):
"""This class is responsible to present info to the user on a terminal."""
@classmethod
def error(self, *message_parts):
print ' '.join(message_parts)
@classmethod
def info(self, *message_parts):
print ' '.join(message_parts)
class Renamer(object):
"""A simple example class which renames files in a directory"""
def __init__(self, old, new, ui=TermUI):
self.old = old
self.new = new
self.ui = ui
def rename(self, dir_):
"""Renames files in the directory tree"""
print "Walking..."
for dirpath, dirnames, filenames in os.walk(dir_):
for filename in filenames:
new_name = filename.replace(self.old, self.new)
if new_name != filename:
try:
#os.rename(join(dirpath, filename),
join(dirpath, new_name))
self.ui.info("Renaming", join(dirpath,
filename), "to", new_name)
except OSError:
self.ui.error("Unable to rename",
join(dirpath, filename), "to", new_name)
if new_dirpath != dirpath:
try:
new_dirpath = dirpath.replace(self.old, self.new)
#os.rename(dirpath, new_dirpath)
self.ui.info("Dir renaming", dirpath, "to", new_dirpath)
except OSError:
self.ui.error("Unable to rename", dirpath, "to",
new_dirpath)
renamer = Renamer(old="_", new=" ")
renamer.rename("/Users/riko/Documents")
Io personalmente non creerei una classe per il renamer di cui sopra.
Lo lascerei come un unica funzione di una dozzina di righe.
--
-enrico
More information about the Python
mailing list