[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