[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