Topic: Gestione delle Eccezioni  (Letto 8716 volte)

0 Utenti e 1 Visitatore stanno visualizzando questo topic.

Offline riko

  • python deus
  • *
  • moderatore
  • Post: 7.447
  • Punti reputazione: 12
    • Mostra profilo
    • RiK0 Tech Temple
Gestione delle Eccezioni
« il: Gennaio 12, 2011, 20:36 »
Sulla ML e' passato un messaggio di Piro con un link ad un suo post su Stacktrace.
Siccome e' IMHO molto interessante, ve lo rigiro qui:

http://stackoverflow.com/questions/497952/how-to-make-python-gracefully-fail/498038#498038

Offline Charles_Stain

  • python sapiens sapiens
  • *
  • moderatore
  • Post: 1.220
  • Punti reputazione: 0
    • Mostra profilo
    • My personal website
Re: Gestione delle Eccezioni
« Risposta #1 il: Gennaio 13, 2011, 08:59 »
 :ok:

Offline ubuntu_of_fortune

  • python sapiens
  • *
  • moderatore
  • Post: 745
  • Punti reputazione: 3
    • Mostra profilo
Re: Gestione delle Eccezioni
« Risposta #2 il: Gennaio 13, 2011, 18:53 »
Grande Piro! :)

Grazie riko per la segnalazione!  :ok:
Davvero interessante!  8)

Offline Python

  • python sapiens sapiens
  • ******
  • Post: 2.045
  • Punti reputazione: 2
  • Radon - Cyclomatic Complexity of your Python code
    • Mostra profilo
    • Radon - Cyclomatic Complexity of your Python code
Re: Gestione delle Eccezioni
« Risposta #3 il: Gennaio 13, 2011, 19:52 »
Interessante, conoscevo il modulo logging ma non l'avevo mai usato...

Offline riko

  • python deus
  • *
  • moderatore
  • Post: 7.447
  • Punti reputazione: 12
    • Mostra profilo
    • RiK0 Tech Temple
Re: Gestione delle Eccezioni
« Risposta #4 il: Gennaio 13, 2011, 20:03 »
Interessante, conoscevo il modulo logging ma non l'avevo mai usato...

Tipicamente e' una cosa di cui senti le esigenze su un progetto "dal medio in su".
Quello che a me *piace* del modulo logging e' che non sparge output per tutto il programma
(il fatto che logging vada su qualche output e' solo una convenzione).

Quello che non mi piace e' comunque quando si finisce per avere logica di logging (qualunque cosa sia)
sparsa nel programma. Vedi AOP. Certo, noi probabilmente ce la si cava con i soliti decoratori.
E messa nella gestione eccezioni non mi disturba punto, in realta'.

Sono comunque questi i momenti in cui mi viene da chiedermi quanto ci perdiamo a non avere i blocchi sintattici.

Offline ubuntu_of_fortune

  • python sapiens
  • *
  • moderatore
  • Post: 745
  • Punti reputazione: 3
    • Mostra profilo
Re: Gestione delle Eccezioni
« Risposta #5 il: Gennaio 13, 2011, 21:00 »
Quello che non mi piace e' comunque quando si finisce per avere logica di logging (qualunque cosa sia)
sparsa nel programma. Vedi AOP. Certo, noi probabilmente ce la si cava con i soliti decoratori.
E messa nella gestione eccezioni non mi disturba punto, in realta'.

Assolutamente d'accordo!  :ok:

Sono comunque questi i momenti in cui mi viene da chiedermi quanto ci perdiamo a non avere i blocchi sintattici.

Cioe`? Che intendi? :)

Offline riko

  • python deus
  • *
  • moderatore
  • Post: 7.447
  • Punti reputazione: 12
    • Mostra profilo
    • RiK0 Tech Temple
Re: Gestione delle Eccezioni
« Risposta #6 il: Gennaio 13, 2011, 22:18 »
Devo trovare il modo di elaborare.
Di fatto mi chiedo se avere qualche feature di piu' potrebbe essere un male o un bene.

Offline morellik

  • python neanderthalensis
  • ****
  • Post: 343
  • Punti reputazione: 2
    • Mostra profilo
Re: Gestione delle Eccezioni
« Risposta #7 il: Gennaio 14, 2011, 09:50 »
Interessantissima tip.

Grazie Riko per la segnalazione.

PS. Mi unisco a ubuntu_of_fortune, cosa intendi per blocchi sintattici.
« Ultima modifica: Gennaio 14, 2011, 09:51 da morellik »

Offline riko

  • python deus
  • *
  • moderatore
  • Post: 7.447
  • Punti reputazione: 12
    • Mostra profilo
    • RiK0 Tech Temple
Re: Gestione delle Eccezioni
« Risposta #8 il: Gennaio 14, 2011, 18:05 »
Essenzialmente sono indeciso se bastino "quelli di Ruby" oppure se non sia meglio un meccanismo piu' generale, come in Lisp.
In generale aggiungere i blocchi "alla ruby" e' una duplicazione di funzionalita' per il 95% delle cose.

In generale sappiamo tutti che in Ruby si scrive abitualmente roba tipo:

[codice]
hash.each_pair do |key, value|
  puts "#{key} is #{value}"
end
[/codice]

Quello che inizia con do e' un *blocco* (non del senso della semplice "struttura a blocchi" e' un elemento del linguaggio,
come una funzione in Python).

Noi in Python questa cosa la facciamo diversamente: in Ruby "passo" il codice da eseguire
al metodo che lo esegue (in questo caso un metodo che fa una visita delle coppie).
Questo esempio in Python si scrive tutto diverso, usando un metodo che *tira fuori* l'iteratore
e noi ci iteriamo sopra. In Ruby "spingiamo" il codice dentro l'iteratore.

Questo e', in effetti, molto simile ad usare una map, per capirci. Di fatto in Python possiamo scrivere
un metodo "each_pair" piuttosto facilmente, ma poi gli dovremmo passare una funzione.

Ora nel caso dell'iterazione, non ci sono problemi. Il metodo di Python e' meno "fancy", ma e' altrettanto
comodo. Per certi versi e' anche piu' potente (di fatto quando ho in mano l'iteratore, posso controllarlo
come voglio, mentre in Ruby questa possibilita' non la ho)[0].

Il discorso e' che "in generale" fra blocchi e funzioni la questione e' che da un lato e' brutto dover definire
una funzione per usarla una volta sola. Dall'altro si finisce per duplicare il codice in piu' blocchi che starebbero
bene astratti in una sola funzione.

Ma la questione che io avevo in mente e' un altra.

Immaginiamo che il nostro metodo quando ha un'eccezione logghi e poi faccia qualcosa di sensato.

In Python possiamo scrivere qualcosa tipo

on_error(log_exception)
def f(...): ...

o anche

on_error(lambda e: logger.log(e#traceback))
def f(...): ...

Tuttavia appena la funzione e' troppo complessa per una lambda bisogna definire
una funzione separata. Questa funzione potrebbe essere una cosa molto generale
(come log_exception) oppure una cosa molto specifica. Nel secondo caso e' brutto
avere:

1. una funzione (con il possibile lato positivo che se riserve almeno l'abbiamo)
2. una funzione definita molto lontana dal punto in questione. Ricordiamo che f
potrebbe essere un metodo e log_error non dovrebbe esserlo.

Questo per l'approccio: usiamo i decoratori per fare aspect oriented programming.
Il fatto e' che la cosa e' molto poco flessibile: di fatto poi ci troviamo nella condizione
in cui *non* possiamo gestire veramente l'errore. Alla fine questa cosa in Python e'
talmente scomoda nella maggior parte dei casi che la soluzione corretta e' usare
un bel po' di try nestati e buone feste a tutti.

Si... ho sproloquiato.




------
  • In generale con l'iteratore faccio davvero quello che voglio.

Le cose sensate pero' sono meno. Tipicamente:

for k, v in palindrome_key(hash.iteritems()):
    print k, v

contro:

hash.each_pair do |k, w|
    if palindrome?(k)
       print ...
    end
end

Come sempre... quando in Python si puo' astrarre in una funzione
(in questo caso la nostra essere palindromo) viene meglio, quando invece
la funzione e' molto ad hoc, viene peggio. Per esempio la
palindrome_key sembra davvero una funzione ad hoc e alla fin fine non mi
e' chiaro se sia meglio della soluzione in ruby come pulizia formale. In generale
la questione e' questa: per logiche di iterazione complicata la manipolazione
diretta dell'iteratore e' un vantaggio, specie se questa cosa si puo' scrivere in
modo pulito. In generale pero' a volte la sintassi del blocco rende piu' leggero
rispetto ad avere una funzione ad hoc.

Offline ubuntu_of_fortune

  • python sapiens
  • *
  • moderatore
  • Post: 745
  • Punti reputazione: 3
    • Mostra profilo
Re: Gestione delle Eccezioni
« Risposta #9 il: Gennaio 26, 2011, 10:36 »
Si... ho sproloquiato.

Macche` anzi, spunto molto interessante..


Essenzialmente sono indeciso se bastino "quelli di Ruby" oppure se non sia meglio un meccanismo piu' generale, come in Lisp.
In generale aggiungere i blocchi "alla ruby" e' una duplicazione di funzionalita' per il 95% delle cose.

Esatto, la penso esattamente in questa maniera...
Ammetto pero` che ragiono piu` da Pythonista che da Rubynista (?? ... Ruby-netto)

Noi in Python questa cosa la facciamo diversamente: in Ruby "passo" il codice da eseguire
al metodo che lo esegue (in questo caso un metodo che fa una visita delle coppie).
Questo esempio in Python si scrive tutto diverso, usando un metodo che *tira fuori* l'iteratore
e noi ci iteriamo sopra. In Ruby "spingiamo" il codice dentro l'iteratore.

Questo e', in effetti, molto simile ad usare una map, per capirci. Di fatto in Python possiamo scrivere
un metodo "each_pair" piuttosto facilmente, ma poi gli dovremmo passare una funzione.

Ora nel caso dell'iterazione, non ci sono problemi. Il metodo di Python e' meno "fancy", ma e' altrettanto
comodo. Per certi versi e' anche piu' potente (di fatto quando ho in mano l'iteratore, posso controllarlo
come voglio, mentre in Ruby questa possibilita' non la ho).

Esatto, quoto!
Hai centrato la questione secondo me... a parte il fatto che una scrittura di questo tipo:
[codice]
hash.each_pair do |key, value|
  puts "#{key} is #{value}"
end
[/codice]

non mi piace proprio (ma sara` sempre per il bias di cui sopra).

Il discorso e' che "in generale" fra blocchi e funzioni la questione e' che da un lato e' brutto dover definire
una funzione per usarla una volta sola. Dall'altro si finisce per duplicare il codice in piu' blocchi che starebbero
bene astratti in una sola funzione.

Beh pensandoci, definire una funzione per utilizzarla una sola volta puo` avere i suoi perche`... Refactoring per esempio...
invece che definire una funzione/metodo con un corpo eccessivamente pregno, e` buona norma frammentare e responsabilizzare le varie parti
 - Sebbene, in generale, queste cose capitano piu` con linguaggi tipo Java che con Python/Ruby[0] - .

Di contro, comunque, rigiro la questione:

In generale astrarre con una funzione utilizzata una sola volta non ha tanto senso, di contro pero`, per ovviare alla mancanza in Python dei blocchi sintattici,
non si puo` all'ccorrenza ricorrere a soluzioni tipo *map*, *lamda expressions* e a "blocchi sintattici" utilizzando lo statement *with* ?

Ma la questione che io avevo in mente e' un altra.
Immaginiamo che il nostro metodo quando ha un'eccezione logghi e poi faccia qualcosa di sensato.
1. una funzione (con il possibile lato positivo che se riserve almeno l'abbiamo)
2. una funzione definita molto lontana dal punto in questione. Ricordiamo che f
potrebbe essere un metodo e log_error non dovrebbe esserlo.

Questo per l'approccio: usiamo i decoratori per fare aspect oriented programming.
Il fatto e' che la cosa e' molto poco flessibile: di fatto poi ci troviamo nella condizione
in cui *non* possiamo gestire veramente l'errore. Alla fine questa cosa in Python e'
talmente scomoda nella maggior parte dei casi che la soluzione corretta e' usare
un bel po' di try nestati e buone feste a tutti.

Ok, concordo! Ma non credi che la questione sia piu` legata al problema che non a Python in generale?
Alla fine, quando cerchi soluzioni *generlaiste* che tentanto di gestire in un unica maniera (unica e apparentemente flessibile) lo stesso problema, perdi sempre qualcosa.
In questo caso la possibilita` di trattare l'errore in maniera specifica per ciascuna situazione.

Una soluzione potrebbe essere quella di utilizzare dei decoratori che invochino un metodo di una istanza di delegate specifica
(immagino di essere stato poco chiaro -  pero` ammetto che e` una idea estemporanea e molto probabilmente eccessivamente sovraingegnerizzata)
 8)


------
  • Esempio tipico
[codice]
File f=new File("test.txt");
FileInputStream fis=new FileInputStream(f);
InputStreamReader isr=new InputStreamReader(fis);
BufferedReader br=new BufferedReader(isr);
String line = br.readLine()
while(line != null) {
       System.out.println(line);
       line=br.readLine();
}
[/codice]

VS

[codice]
file f = open("test.txt")
for l in f:
    print l
[/codice]

EDIT

Vabeh, ammetto che la soluzione in Java si puo` compattare ancora.... per correttezza compatto

[codice]
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(new File("test.txt"))));
String line = br.readLine()
while(line != null) {
       System.out.println(line);
       line=br.readLine();
}
[/codice]

Ecco... leggibile soprattutto! :D
« Ultima modifica: Gennaio 26, 2011, 10:41 da ubuntu_of_fortune »

Offline Python

  • python sapiens sapiens
  • ******
  • Post: 2.045
  • Punti reputazione: 2
  • Radon - Cyclomatic Complexity of your Python code
    • Mostra profilo
    • Radon - Cyclomatic Complexity of your Python code
Re: Gestione delle Eccezioni
« Risposta #10 il: Gennaio 26, 2011, 19:50 »
Tutti quei new mi fanno vomitare -.-
Sarà per quello che ho scelto Python! :D

Offline riko

  • python deus
  • *
  • moderatore
  • Post: 7.447
  • Punti reputazione: 12
    • Mostra profilo
    • RiK0 Tech Temple
Re: Gestione delle Eccezioni
« Risposta #11 il: Gennaio 30, 2011, 15:12 »
Hai centrato la questione secondo me... a parte il fatto che una scrittura di questo tipo:
[codice]
hash.each_pair do |key, value|
  puts "#{key} is #{value}"
end
[/codice]

non mi piace proprio (ma sara` sempre per il bias di cui sopra).

Esatto. Siamo completamente nel mondo dei gusti personali.
Sull'esempietto qua sopra siamo molto vicini come scrittura all'equivalente Python.
La differenza e' che in Python abbiamo una special form (e le special forms in python non si aggiungo)
e in ruby abbiamo un metodo (e i metodi in ruby -- e in python -- si aggiungono liberamente).

Per il resto e' un rimescolamento di termini...


Citazione
Il discorso e' che "in generale" fra blocchi e funzioni la questione e' che da un lato e' brutto dover definire
una funzione per usarla una volta sola. Dall'altro si finisce per duplicare il codice in piu' blocchi che starebbero
bene astratti in una sola funzione.

Beh pensandoci, definire una funzione per utilizzarla una sola volta puo` avere i suoi perche`... Refactoring per esempio...
invece che definire una funzione/metodo con un corpo eccessivamente pregno, e` buona norma frammentare e responsabilizzare le varie parti
 - Sebbene, in generale, queste cose capitano piu` con linguaggi tipo Java che con Python/Ruby[0] - .

Sono assolutamente d'accordo sul caso generale. Definire una funzione per refactoring o per documentazione e' sicuramente
una cosa buona e ha senso, anche se la funzione e' usata una sola volta (dopotutto quando si progetta una libreria, molte
funzioni sono usate 0 volte... le useranno i clients). Conversamente, immagina di "dovere" trasformare i body dei for in Python
in funzioni "obbligatoriamente".

Siamo d'accordo che ci sembrerebbe molto strana come pratica. Con il for non c'e' problema, ma se usiamo uno stile
un po' piu' funzionale di programmazione con lc e combriccola, a volte il desiderio di qualcosa di piu' ricco in una
funzione si pone. Questo essenzialmente traccia in Python il limite di buon senso su quando si *deve* usare un for.

Con i blocchi alla ruby questo limite sarebbe probabilmente 2 tacche avanti. Non credo niente di drammatico.

[codice]In generale astrarre con una funzione utilizzata una sola volta non ha tanto senso, di contro pero`, per ovviare alla mancanza in Python dei blocchi sintattici,
non si puo` all'ccorrenza ricorrere a soluzioni tipo *map*, *lamda expressions* e a "blocchi sintattici" utilizzando lo statement *with* ? [/codice]

Ci ricolleghiamo a sopra. Esatto! La differenza e' che tutta la parte funzionale di Python e' "volutamente" tenuta limitata.
E mi sta bene, per carita': quando si comincia a fare eccessivamente i fighi con quella parte di Python si scrive codice molto poco leggibile.
Credo che sia una questione sintattica: in Lisp le stesse cose sono molto piu' umane. Quindi si, sono d'accordo.

Poi with vince a mani basse sui blocchi per le cose che si fanno bene con i blocchi. *Ma* e' un po' piu' verboso da mettere giu'.

Provo a spiegare: quando si usa un "prefabbricato" with e' il massimo:

[codice]
with context() as c:
    do_something(c)
[/codice]

e' ottimo. Specie nei casi semplici:
[codice]
with file(...) as f:
    for line in f:
        process_line(line)[/codice]

e' veramente bello. Guardiamolo in Ruby:
[codice]
File.open('log.txt') do |file_object|
    file_object.each_line do |line|
        print line
    end
end
[/codice]

ancora una volta la forma e' veramente simile. Ancora una volta abbiamo un metodo
contro una special form. Qui si vedono due cose essenzialmente: io credo che il with
sia leggermente piu' chiaro.

La versione Ruby e' piu' facile da scrivere: usare i blocchi per il pattern

[codice]def f(...)
   setup
   begin
       injected_block
   ensure
       cleanup
   end
end[/codice]

e' davvero facile e comodo. In particolare i blocchi in ruby si usavano gia' per fare queste cose
quando noi non avevamo ancora with. All'epoca in effetti un po' invidiavo questa cosa. Scriversi
da 0 questo tipo di pattern (ovvero un context manager) e' parecchio piu' complesso *concettualmente*
esattamente come e' piu' comodo da usare.

Questo, se ci pensiamo, e' *esattamente* lo stesso pattern degli iteratori. Gli iteratori li abbiamo
semplificati con i generatori: due botte di yield e diventano semplici da scrivere.

Citazione
Ok, concordo! Ma non credi che la questione sia piu` legata al problema che non a Python in generale?

Si, certo.

Citazione
Alla fine, quando cerchi soluzioni *generlaiste* che tentanto di gestire in un unica maniera (unica e apparentemente flessibile) lo stesso problema, perdi sempre qualcosa.
In questo caso la possibilita` di trattare l'errore in maniera specifica per ciascuna situazione.

Ni. Qui entra l'arte del design: ci sono, immagino, esempi di problemi su cui non ci si puo' fare nulla.
Pero' spesso il problema e' che non si riesce a farlo mantenendo la feature *semplice*.

Per esempio... possiamo ridurre *ogni* forma di controllo di flusso ad un singolo costrutto? Si. Possiamo.
First class continuations. Ci fai eccezioni, backtracking, qualunque cosa. Cosa paghi? Paghi che il mondo sarebbe un posto migliore perche' i cialtroni non riuscirebbero a fare i programmatori e noi prenderemmo stipendi 10 volte piu' alti. Ma a parte questo perderemmo il fatto che la maggior parte della gente non saprebbe programmare (e di fatto qualcuno inventerebbe dei linguaggi umani per queste persone tornando al mondo odierno).

Citazione
Una soluzione potrebbe essere quella di utilizzare dei decoratori che invochino un metodo di una istanza di delegate specifica
(immagino di essere stato poco chiaro -  pero` ammetto che e` una idea estemporanea e molto probabilmente eccessivamente sovraingegnerizzata)
 8)

Interessante... un esempio?

Citazione
------
  • Esempio tipico
[codice]
File f=new File("test.txt");
FileInputStream fis=new FileInputStream(f);
InputStreamReader isr=new InputStreamReader(fis);
BufferedReader br=new BufferedReader(isr);
String line = br.readLine()
while(line != null) {
       System.out.println(line);
       line=br.readLine();
}
[/codice]

VS

[codice]
file f = open("test.txt")
for l in f:
    print l
[/codice]

EDIT

Vabeh, ammetto che la soluzione in Java si puo` compattare ancora.... per correttezza compatto

[codice]
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(new File("test.txt"))));
String line = br.readLine()
while(line != null) {
       System.out.println(line);
       line=br.readLine();
}
[/codice]

Ecco... leggibile soprattutto! :D
[/quote]

Lol. Quoto.

Ma in Java anche questo ha un suo fascino (ecco il lato dinamico e funzionale di Java).

[codice=Java]
import java.io.*;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class Prova {
    static public void main(String[] s) throws FileNotFoundException {
        final BufferedReader br = new BufferedReader(new FileReader("/Users/enrico/log.txt"));
        for(String line : new Iterable<String>() {
            public Iterator<String> iterator() {
                return new Iterator<String>() {
                    private String bufferedLine = null;
                    private boolean good = true;
                    public boolean hasNext() {
                        if(bufferedLine == null) {
                            try {
                                bufferedLine = br.readLine();
                            } catch (IOException e) {
                                good = false;
                                return false;
                            }
                        }
                        return good && (bufferedLine != null);
                    }

                    public String next() {
                        if(!good) { throw new NoSuchElementException(); }
                        if(bufferedLine != null) {
                            String tmp = bufferedLine;
                            bufferedLine = null;
                            return tmp;
                        } else {
                            try {
                                String tmp = br.readLine();
                                if(tmp != null) {
                                    return tmp;
                                } else {
                                    good = false;
                                    throw new NoSuchElementException();
                                }
                            } catch (IOException e) {
                                good = false;
                                throw new NoSuchElementException();
                            }
                        }
                    }

                    public void remove() {
                        throw new UnsupportedOperationException();
                    }
                };
            }
        }) {
            System.out.println(line);

        }
       
    }
}
[/codice]

In clojure...
[codice]
(import [java.io BufferedReader FileReader])

(with-open [f (BufferedReader. (FileReader. "log.txt"))]
  (doseq [line (line-seq f)]
    (println line)))
[/codice]

Offline ubuntu_of_fortune

  • python sapiens
  • *
  • moderatore
  • Post: 745
  • Punti reputazione: 3
    • Mostra profilo
Re: Gestione delle Eccezioni
« Risposta #12 il: Febbraio 03, 2011, 22:12 »
La differenza e' che in Python abbiamo una special form (e le special forms in python non si aggiungo)
e in ruby abbiamo un metodo (e i metodi in ruby -- e in python -- si aggiungono liberamente).

Per il resto e' un rimescolamento di termini...

Si esatto! Siamo perfettamente concordi allora :)

Conversamente, immagina di "dovere" trasformare i body dei for in Python in funzioni "obbligatoriamente".

Siamo d'accordo che ci sembrerebbe molto strana come pratica. Con il for non c'e' problema, ma se usiamo uno stile
un po' piu' funzionale di programmazione con lc e combriccola, a volte il desiderio di qualcosa di piu' ricco in una
funzione si pone. Questo essenzialmente traccia in Python il limite di buon senso su quando si *deve* usare un for.
Si certo! Come sopra, assolutamente d'accordo!

Poi with vince a mani basse sui blocchi per le cose che si fanno bene con i blocchi. *Ma* e' un po' piu' verboso da mettere giu'.
io credo che il with sia leggermente piu' chiaro.

La versione Ruby e' piu' facile da scrivere: usare i blocchi per il pattern

[codice]def f(...)
   setup
   begin
       injected_block
   ensure
       cleanup
   end
end[/codice]

e' davvero facile e comodo.

Si infatti! Trovo la sintassi del *with* notevolmente piu` chiara e intuitiva dei blocchi alla Ruby - ma, per ribadire: questione di abitudini e gusti :)

Questo, se ci pensiamo, e' *esattamente* lo stesso pattern degli iteratori. Gli iteratori li abbiamo
semplificati con i generatori: due botte di yield e diventano semplici da scrivere.
+1

Per esempio... possiamo ridurre *ogni* forma di controllo di flusso ad un singolo costrutto? Si. Possiamo.
First class continuations. Ci fai eccezioni, backtracking, qualunque cosa. Cosa paghi? Paghi che il mondo sarebbe un posto migliore perche' i cialtroni non riuscirebbero a fare i programmatori e noi prenderemmo stipendi 10 volte piu' alti. Ma a parte questo perderemmo il fatto che la maggior parte della gente non saprebbe programmare (e di fatto qualcuno inventerebbe dei linguaggi umani per queste persone tornando al mondo odierno).

LOL

Ma in Java anche questo ha un suo fascino (ecco il lato dinamico e funzionale di Java).
[codice=Java]
import java.io.*;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class Prova {
    static public void main(String[] s) throws FileNotFoundException {
        final BufferedReader br = new BufferedReader(new FileReader("/Users/enrico/log.txt"));
        for(String line : new Iterable<String>() {
            public Iterator<String> iterator() {
                return new Iterator<String>() {
                    private String bufferedLine = null;
                    private boolean good = true;
                    public boolean hasNext() {
                        if(bufferedLine == null) {
                            try {
                                bufferedLine = br.readLine();
                            } catch (IOException e) {
                                good = false;
                                return false;
                            }
                        }
                        return good && (bufferedLine != null);
                    }

                    public String next() {
                        if(!good) { throw new NoSuchElementException(); }
                        if(bufferedLine != null) {
                            String tmp = bufferedLine;
                            bufferedLine = null;
                            return tmp;
                        } else {
                            try {
                                String tmp = br.readLine();
                                if(tmp != null) {
                                    return tmp;
                                } else {
                                    good = false;
                                    throw new NoSuchElementException();
                                }
                            } catch (IOException e) {
                                good = false;
                                throw new NoSuchElementException();
                            }
                        }
                    }

                    public void remove() {
                        throw new UnsupportedOperationException();
                    }
                };
            }
        }) {
            System.out.println(line);

        }
       
    }
}
[/codice]

Si certo, ha un suo fascino... il fascino del surreale! :D
Scherzi a parte, trovo che l'introduzione del *for each* in Java5+ abbia notevolmente semplificato e alleggerito (soprattutto) l'iterazione sulle collezioni.
In Java una scrittura del genere ha i suoi perche` con un vasto numero di classi interne... certo che replicare le stesse istruzioni per ogni singola lettura di file e` da manicomio! :)

In clojure...
[codice]
(import [java.io BufferedReader FileReader])

(with-open [f (BufferedReader. (FileReader. "log.txt"))]
  (doseq [line (line-seq f)]
    (println line)))
[/codice]

Questa, invece la trovo una *Grande* alternativa! ;)
Bello Clojure... devo dargli una occhiata! :)

[... continuo a rispondere in un altro post ...]

Offline ubuntu_of_fortune

  • python sapiens
  • *
  • moderatore
  • Post: 745
  • Punti reputazione: 3
    • Mostra profilo
Re: Gestione delle Eccezioni
« Risposta #13 il: Febbraio 03, 2011, 22:59 »
Citazione
Una soluzione potrebbe essere quella di utilizzare dei decoratori che invochino un metodo di una istanza di delegate specifica
(immagino di essere stato poco chiaro -  pero` ammetto che e` una idea estemporanea e molto probabilmente eccessivamente sovraingegnerizzata)
 8)

Interessante... un esempio?

Non ricordo esattamente cosa avevo in mente, ma ho cercato di ricostruire - la mia idea estemporanea era quella di giocare con una gerarchia di gestori di eccezioni, ciascuno responsabilizzato alla gestione di una determinata eccezione e assegnare il controllo delle eccezioni via decoratori.

Ancora una volta, questo avvalora la tesi di Riko che in Python per "iniettare" codice da eseguire, si devono utilizzare funzioni o *riferimenti a funzioni* (in questo caso decoratori).

Questa la mia soluzione (provo a spiegarla passo passo):
  • 1) Definizione di Eccezioni Custom

Come prima cosa, definiamo le Eccezioni personalizzate

[codice]
# Custom Exceptions
class GenericException(Exception):
    pass

class CustomException(Exception):
    pass
[/codice]

  • 2) Definizione dei Gestori delle Eccezioni

A questo punto, una volta definite le eccezioni custom, e` necessario definire le classi delegate alla gestione delle eccezioni le quali rappresenteranno l'input da passare al decoratore

[codice]
# Custom Exception Handlers
class GenericExceptionHandler(object):
   
    def do_try(self, func, *pargs, **kwargs):
        return func(*pargs, **kwargs)
   
    def __call__(self, func, *pargs, **kwargs):
        try:
            self.do_try(func, *pargs, **kwargs)
        except GenericException as gex:
            print 'Generic Exception:', gex
        except Exception as ex:
            print 'Simple Exception:', ex
           
class CustomExceptionHandler(GenericExceptionHandler):
   
    def do_try(self, func, *pargs, **kwargs):
        try:
            return func(*pargs, **kwargs)
        except CustomException as cex:
            print 'Custom Exception:', cex
[/codice]

L'idea era quella di definire una gerarchia di Handler di eccezioni (eventualmente estendibili e ulteriormente customizzabili con un Template Method Pattern)
che racchiudono le esecuzioni della funzione (successivamente decorata) in una serie di *try-except* innestati.

Per come sono implementati, gli handler sono dei callable che attivano il controllo delle eccezioni quando sono invocati.

  • 3) Definizione del decoratore

[codice]
#decorator
def check_exceptions(handler):
    class Dispatcher(object):
        def __init__(self, func):
            self.func = func
            self.handler = handler()
        def __call__(self, *pargs, **kwargs):
            return self.handler(self.func, *pargs, **kwargs)
    return Dispatcher
[/codice]

Il decoratore ha come parametro aggiuntivo l'handler a cui delegare la gestione delle eccezioni.

  • 4) Codice di Esempio

[codice]
#decorated Functions
@check_exceptions(GenericExceptionHandler)
def raise_generic_exception():
    raise GenericException('Generic Exception and Generic Handler')

@check_exceptions(CustomExceptionHandler)
def raise_custom_exception():
    raise CustomException('Custom Exception and Custom Handler')

@check_exceptions(CustomExceptionHandler)
def raise_generic_exception_with_custom_handler():
    raise GenericException('Generic Exception and Custom Handler (nested try works)')
   
@check_exceptions(GenericExceptionHandler)
def raise_custom_exception_with_generic_handler():
    raise CustomException('Custom Exception and Generic Handler (caught Simple Exception)')

def no_exception_handling():
    raise Exception('Not handled Exception')
   
if __name__ == '__main__':
    raise_generic_exception()
    raise_custom_exception()
    raise_generic_exception_with_custom_handler()
    raise_custom_exception_with_generic_handler()
    no_exception_handling()

[/codice]

da notare un paio di cose:
prima di tutto e` importante notare che la classe GenericExceptionHandler ha un except anche per Exception. Questo di solito e` sempre buona norma farlo per la gestione di eccezioni generiche.
Di contro pero`, evitare (come la peste) l'uso di except generici senza specificare alcun tipo di eccezione.
Non e` un buon idioma e si presta alla gestione di qualunque (troppe) situazione.

Ancora sulla gestione delle eccezione: le ultime due funzioni decorate invertono l'handler con il tipo di Eccezione lanciata.
Se nel primo caso (Handler Specializzato e Eccezione Generica), l'eccezione Generica viene catturata - provando di fatto l'effettivo funzionamento dei try innestati; nel secondo caso (Handler Generico ed Eccezione Specializzata)
interviene l'except sul tipo *Exception* descritto prima che salva capra e cavoli!

Infine, la seconda cosa che vorrei evidenziare e` che gli handler, in quanto classi Callable, potevano tranquillamente essere loro stessi eletti a decoratori (modificati in modo che nel metodo costruttore memorizzassero il riferimento alla funzione decorata, esattamente come fatto dalla classe Dispatcher nel decoratore).
Non l'ho fatto, in quanto avrebbero funzionato (as-is) solo nel caso di funzioni ma non per la decorazione di metodi - invito tutte le nuove leve a capire il perche` !

Spero di essermi riuscito a spiegare!
Btw, questa e` solo una idea estemporanea, ma credo esponga il fianco ad ogni possibile margine di miglioramento! :)
« Ultima modifica: Febbraio 04, 2011, 09:38 da ubuntu_of_fortune »