2009-05-14 2 views
11

In un answer (da S.Lott) a una domanda sulla try...else dichiarazione di Python:"else" è considerato dannoso in Python?

In realtà, anche a se-dichiarazione, l'altro : può essere abusata in veramente terribili modi creando bug che sono molto difficili da trovare. [...]

Pensateci due volte prima di altro :. si tratta di generalmente un problema. Evitare tranne in un se-dichiarazione e anche allora considerano documentare la altrove condizione relativa alla è esplicito.

È un'opinione diffusa? elseconsidered harmful?

Naturalmente è possibile scrivere codice confuso, ma è vero per qualsiasi altro costrutto linguistico. Anche Python for...else mi sembra una cosa molto utile da avere (meno così per try...else).

+0

Ciao dF. In una simile zona di pericolo: a = b = c = 2 –

+1

E per quanto riguarda i loop, potrebbero girare per sempre quando non li vuoi. Sembra un po 'esagerato se ti sbarazzi delle funzionalità linguistiche potenzialmente pericolose. – Milhous

risposta

30

S.Lott ha ovviamente visto un codice errato. Non abbiamo tutti? Non considero altro dannoso, anche se l'ho visto usato per scrivere codice cattivo. In quei casi, anche il codice circostante è stato male, quindi perché incolpare altri poveri?

+7

Concordato: è troppo facile incolpare gli strumenti e non la persona che li usa –

15

No, non è dannoso, è necessario.

Ci dovrebbe sempre essere una dichiarazione catch-all. Tutti gli switch dovrebbero avere un valore predefinito. Tutti i pattern matching in un linguaggio ML dovrebbero avere un valore predefinito.

L'argomento secondo cui è impossibile ragionare su ciò che è vero dopo una serie di dichiarazioni if ​​è un fatto della vita. Il computer è la più grande macchina a stati finiti là fuori, ed è sciocco enumerare ogni singola possibilità in ogni situazione.

Se hai davvero paura che errori sconosciuti passino inosservati in altre dichiarazioni, è davvero così difficile sollevare un'eccezione lì?

+3

Non concordo. Ci dovrebbe * mai * essere un catch-all SE non si può effettivamente articolare la logica che porta al catchall. Se non riesci a definire esattamente quale condizione porti al catch-all, hai bug. –

+13

@ S.Lo quindi, anziché rilevare le condizioni sconosciute nell'istruzione else, lo lasceresti cadere ed eseguirai tutto il codice lasciato nella funzione? – Unknown

+1

Personalmente, ho rifattorizzato il codice fino a quando non ha più percorsi di codice in cui il programmatore sta fondamentalmente dicendo "Non so come sono arrivato qui, ma farò comunque qualcosa, probabilmente funzionerà". Se quel "qualcosa" è quello di aumentare ProgrammerNotSmartEnoughException, o di continuare a eseguire il resto del programma, hai un problema. Forse un problema più piccolo nel caso dell'eccezione, ma ancora un problema. –

6

Per me, l'intero concetto di alcuni costrutti del linguaggio popolare che sono intrinsecamente cattivi è semplicemente sbagliato. Anche il goto ha il suo posto. Ho visto codice molto leggibile e gestibile da artisti come Walter Bright e Linus Torvalds che lo usano. È molto meglio semplicemente insegnare ai programmatori che conta la leggibilità e usare il buon senso piuttosto che dichiarare arbitrariamente certi costrutti "dannosi".

+2

E penso che chiamare altrimenti "dannoso" stia andando troppo lontano. Ho solo detto di pensare e rendere esplicita la condizione. Non ho detto "non usarlo mai". –

7

Dire che il resto è considerato dannoso è un po 'come dire che le variabili o le classi sono dannose. Diamine, è come dire che goto è dannoso. Certo, le cose possono essere abusate. Ma ad un certo punto, devi solo fidarti dei programmatori per essere adulti ed essere abbastanza intelligente da non farlo.

Quello che succede è questo: se sei disposto a non usare qualcosa perché una risposta su SO o un post di un blog o anche un famoso articolo di Dijkstra ti ha detto di non farlo, devi considerare se la programmazione è la giusta professione per te

+0

+1: sì, anche goto ha ancora un posto. Quando è stato scritto "GOTO Considerato Nocivo", era necessario lo sproloquio, ma sembra che le lezioni siano state apprese. – dwc

+1

No: il resto non è lo stesso livello di danno. Il resto è intenzionalmente vago. Può essere un modo sciatto di dire "Non riesco a capire le condizioni, quindi spero solo che quelle giuste cadano nel mio altro". Se stai facendo quel tipo di non-pensiero sciatto, stai scrivendo bug intenzionali. –

+4

@ S.Lott - Penso che manchi il mio punto. Il mio punto è che la gente ha bisogno di allontanarsi dalla mentalità "x è cattivo, non usarlo". SOPRATTUTTO in termini di costrutti linguistici. –

7

Non direi che è dannoso, ma ci sono momenti in cui la dichiarazione else può metterti nei guai. Ad esempio, se è necessario eseguire alcune elaborazioni in base a un valore di input e ci sono solo due valori di input validi. Solo il controllo per uno potrebbe introdurre un bug. es:

The only valid inputs are 1 and 2: 

if(input == 1) 
{ 
    //do processing 
    ... 
} 
else 
{ 
    //do processing 
    ... 
} 

In questo caso, usando l'altro permetterebbe tutti i valori diversi da 1 da lavorare quando dovrebbe essere solo per valori 1 e 2.

+3

in questo caso avresti dovuto fare altrimenti if (input == 2) e poi avere un altro {throw InvalidInput} – Unknown

+0

Esattamente. Sono stato un po 'da questo errore molte volte quando provavo a fare manutenzione su applicazioni legacy. Cerco di usare altrimenti semplicemente per generare eccezioni per dati non validi, cioè a meno che tutto ciò di cui ho bisogno sia una singola istruzione if. –

+0

nan, avrei solo altro se (input == 2) e nient'altro, quindi non succederà nulla se l'input non è 1 o 2 ... quindi buon esempio +1 – TStamper

0

Nell'esempio postulato di essere difficile motivo, può essere scritto esplicitamente, ma il resto è ancora necessario. E.g.

if a < 10:  
    # condition stated explicitly 
elif a > 10 and b < 10:  
    # condition confusing but at least explicit 
else:  
    # Exactly what is true here?  
    # Can be hard to reason out what condition is true 

può essere scritto

if a < 10:  
    # condition stated explicitly 
elif a > 10 and b < 10:  
    # condition confusing but at least explicit 
elif a > 10 and b >=10: 
    # else condition 
else: 
    # Handle edge case with error? 
+0

-1: la seconda versione non è più chiara della prima. Entrambi senza il motivo del codice, potrebbero essere validi. –

+1

Rende esplicite tutte le opzioni, la clausola else viene chiamata solo quando c'è un errore nella logica delle prime tre opzioni. ancora un esempio abbastanza privo di significato sicuro. – Nat

+1

Si consideri il programmatore di manutenzione che modifica alcune clausole ma non pensa attraverso la logica (oscura). Ora il "caso limite" inizia improvvisamente a causa di scadenti cambi di codice. Lanciare il caso limite in un altro porterà a misteri. La gestione dell'errore impensabile (nell'esempio 2) esporterà immediatamente l'errore di manutenzione. –

3

Else è più utile quando documentare ipotesi circa il codice. Assicura di aver pensato attraverso entrambi i lati di una dichiarazione if.

Utilizzare sempre una clausola else con ciascuna istruzione if è anche una pratica consigliata in "Codice completo".

+0

Sì, sono arrivato a pensare a questa affermazione nel codice Completa! Mi sento un po 'danneggiato per averlo ricordato! –

2

La logica alla base della dichiarazione else (di try...else) in Python, in primo luogo, era quella di individuare solo le eccezioni desiderate. Normalmente quando si ha un blocco try...except, c'è del codice che potrebbe generare un'eccezione, e poi c'è un altro codice che dovrebbe essere eseguito solo se il codice precedente ha avuto successo. Senza un blocco else, che avrebbe dovuto mettere tutto quel codice nel blocco try:

try: 
    something_that_might_raise_error() 
    do_this_only_if_that_was_ok() 
except ValueError: 
    # whatever 

Il problema è, che cosa se do_this_only_if_that_was_ok() solleva una ValueError? Sarebbe stato catturato dall'istruzione except, quando potresti non averlo desiderato. Questo è lo scopo del blocco else:

try: 
    something_that_might_raise_error() 
except ValueError: 
    # whatever 
else: 
    do_this_only_if_that_was_ok() 

Credo che sia una questione di opinione in una certa misura, ma io personalmente che questa sia una grande idea, anche se io lo uso molto raramente. Quando lo uso, mi sembra molto appropriato (e inoltre, penso che aiuti a chiarire un po 'il flusso del codice)

+0

Non posso fare a meno di pensare che se si vuole che do_this_only_if_that_was_ok' venga chiamato in entrambi i modi e si pensa che possa generare un errore di valore, sarebbe più chiaro aggiungerlo a un'istruzione separata (supponendo che si stia tentando di separarli fuori quello è). –

+0

Ma non voglio che venga chiamato in entrambi i modi (ad esempio, se l'eccezione è stata sollevata o meno), voglio che venga chiamato solo se qualcosa_che_punto_serrato_error() non ha generato un errore. E se do_this_only_if_that_was_ok() solleva un errore, non dovrebbe essere catturato (almeno non a questo livello). Non vedo alcun modo per farlo a parte il modo in cui ho scritto qui. –

3

Au contraire ... A mio parere, DEVE esserci un altro per ogni if. Certo, puoi fare cose stupide, ma puoi abusare di qualsiasi costrutto se ci provi abbastanza. Conoscete il detto "un vero programmatore può scrivere FORTRAN in ogni lingua".

Quello che faccio molto tempo è scrivere la parte else come commento, descrivendo perché non c'è niente da fare.

1

Mi sembra che, per qualsiasi lingua e qualsiasi dichiarazione di controllo del flusso in cui vi sia uno scenario predefinito o un effetto collaterale, tale scenario deve avere lo stesso livello di considerazione. La logica in if o switch o while è valida solo come condizione se (x) while (x) o for (...). Pertanto la dichiarazione non è dannosa ma la logica nelle loro condizioni è.

Pertanto, come sviluppatori è nostra responsabilità codificare con la vasta gamma di altri in-mind. Troppi sviluppatori lo considerano un "se non il precedente" quando di fatto può ignorare tutto il senso comune perché l'unica logica in esso è la negazione della logica precedente, che è spesso incompleta. (un errore di progettazione dell'algoritmo stesso)

Non ritengo quindi che 'else' sia più dannoso che off-by-one in un ciclo for() o gestione della memoria errata. È tutto sugli algoritmi. Se i tuoi automi sono completi nella sua portata e possibili rami e sono tutti concreti e comprensibili, non c'è pericolo.Il pericolo è l'abuso della logica dietro le espressioni da parte delle persone che non realizzano l'impatto della logica ad ampio raggio. I computer sono stupidi, fanno quello che viene detto dal loro operatore (in teoria)

faccio considerano il cercare e cattura essere pericoloso perché può negare la manipolazione di una quantità sconosciuta di codice. La ramificazione sopra l'aumento potrebbe contenere un bug, evidenziato dallo alza stesso. Questo può essere non ovvio. È come trasformare un insieme sequenziale di istruzioni in un albero o grafico di gestione degli errori, in cui ciascun componente dipende dai rami nel genitore. Dispari. Intendiamoci, io amo C.

0

Credo che il punto rispetto al try...except...else è che si tratta di un errore facile da usare per creare stato incoerente, piuttosto che risolvere il problema. Non è che dovrebbe essere evitato a tutti i costi, ma può essere controproducente.

considerare:

try: 
    file = open('somefile','r') 
except IOError: 
    logger.error("File not found!") 
else: 
    # Some file operations 
    file.close() 
# Some code that no longer explicitly references 'file' 

Sarebbe davvero bello dire che il blocco di cui sopra ha impedito di codice di tentare di accedere a un file che non esisteva, o di una directory per i quali l'utente non ha i permessi, e per dire che tutto è incapsulato perché è all'interno di un blocco try...except...else. Ma in realtà, un sacco di codice nel modulo sopra in realtà dovrebbe essere simile a questo:

try: 
    file = open('somefile','r') 
except IOError: 
    logger.error("File not found!") 
    return False 
# Some file operations 
file.close() 
# Some code that no longer explicitly references 'file' 

Siete spesso ingannare se stessi dicendo che a causa file non fa più riferimento di portata, è bene andare sulla codifica dopo la blocco, ma in molti casi qualcosa verrà fuori dove non va bene. O forse una variabile verrà successivamente creata all'interno del blocco else che non è stato creato nel blocco except.

In questo modo dovrei differenziare lo if...else da try...except...else. In entrambi i casi, è necessario rendere i blocchi paralleli nella maggior parte dei casi (le variabili e lo stato impostato in uno dovrebbero essere impostati nell'altro), ma nel secondo caso, i programmatori spesso non lo fanno, probabilmente perché è impossibile o irrilevante. In questi casi, spesso sarà più sensato tornare al chiamante piuttosto che cercare di continuare a lavorare su ciò che pensi di avere nel migliore dei casi.

4

se si scrive:

if foo: 
    # ... 
elif bar: 
    # ... 
# ... 

allora il lettore può essere lasciato chiedersi: che cosa succede se non foobar è vero? Forse sapete, dalla vostra comprensione del codice, che deve essere il caso che sia foo o bar. Io preferirei vedere:

if foo: 
    # ... 
else: 
    # at this point, we know that bar is true. 
    # ... 
# ... 

o:

if foo: 
    # ... 
else: 
    assert bar 
    # ... 
# ... 

Ciò rende chiaro al lettore come ci si aspetta il controllo di flusso, senza richiedere al lettore di avere una profonda conoscenza di dove foo e bar venire a partire dal.

(nel caso originale, si potrebbe ancora scrivere un commento che spiega cosa sta succedendo, ma penso che mi chiederei allora: "Perché non usare solo una clausola else:?")

Credo che il punto non è che non si deve usare else:, anzi, che una clausola else: può permettere di scrivere codice chiaro e si dovrebbe cercare di riconoscere quando questo accade e aggiungere un piccolo commento per aiutare tutti i lettori

che è vero per la maggior parte delle cose in linguaggi di programmazione, in realtà :-)

+2

+1: se pippo: qualcosa; altro: assert bar; qualcos'altro. Rendilo esplicito in qualche modo. –

1

C'è un cosiddetto "penzoloni altro" problema che si incontra nelle lingue della famiglia C come segue:.

if (a==4) 
if (b==2) 
printf("here!"); 
else 
printf("which one"); 

Questo codice innocente può essere inteso in due modi:

if (a==4) 
    if (b==2) 
     printf("here!"); 
    else 
     printf("which one"); 

o

if (a==4) 
    if (b==2) 
     printf("here!"); 
else 
    printf("which one"); 

Il problema è che il "resto" è "penzoloni", si può confondere il proprietario della cosa. Ovviamente il compilatore non farà questa confusione, ma è valido per i mortali.

Grazie a Python, non possiamo avere un problema altro penzoloni in Python da quando dobbiamo scrivere sia

if a==4: 
    if b==2: 
     print "here!" 
else: 
    print "which one" 

o

if a==4: 
    if b==2: 
     print "here!" 
    else: 
     print "which one" 

modo che l'occhio umano afferra. E, no, non penso che "altro" sia dannoso, sia dannoso quanto "se".