Programmazione Python > Base

Gestione delle Eccezioni

<< < (2/3) > >>

ubuntu_of_fortune:

--- Citazione da: riko - Gennaio 13, 2011, 20:03 ---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'.

--- Termina citazione ---

Assolutamente d'accordo!  :ok:


--- Citazione da: riko - Gennaio 13, 2011, 20:03 ---Sono comunque questi i momenti in cui mi viene da chiedermi quanto ci perdiamo a non avere i blocchi sintattici.

--- Termina citazione ---

Cioe`? Che intendi? :)

riko:
Devo trovare il modo di elaborare.
Di fatto mi chiedo se avere qualche feature di piu' potrebbe essere un male o un bene.

morellik:
Interessantissima tip.

Grazie Riko per la segnalazione.

PS. Mi unisco a ubuntu_of_fortune, cosa intendi per blocchi sintattici.

riko:
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.




------
[0] 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.

ubuntu_of_fortune:

--- Citazione da: riko - Gennaio 14, 2011, 18:05 ---Si... ho sproloquiato.

--- Termina citazione ---

Macche` anzi, spunto molto interessante..



--- Citazione da: riko - 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.

--- Termina citazione ---

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


--- Citazione da: riko - Gennaio 14, 2011, 18:05 ---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).

--- Termina citazione ---

Esatto, quoto!
Hai centrato la questione secondo me... a parte il fatto che una scrittura di questo tipo:

--- Citazione da: riko - Gennaio 14, 2011, 18:05 ---[codice]
hash.each_pair do |key, value|
  puts "#{key} is #{value}"
end
[/codice]

--- Termina citazione ---

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


--- Citazione da: riko - Gennaio 14, 2011, 18:05 ---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.

--- Termina citazione ---

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* ?


--- Citazione da: riko - Gennaio 14, 2011, 18:05 ---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.

--- Termina citazione ---

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)


------
[0] 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

Navigazione

[0] Indice dei post

[#] Pagina successiva

[*] Pagina precedente

Vai alla versione completa