Domanda

Recentemente mi sono insegnato a Python e ho scoperto i modi di dire LBYL / EAFP per quanto riguarda il controllo degli errori prima dell'esecuzione del codice. In Python, sembra che lo stile accettato sia EAFP e sembra funzionare bene con il linguaggio.

LBYL ( L ook B prima di Y o L EAP ):

def safe_divide_1(x, y):
    if y == 0:
        print "Divide-by-0 attempt detected"
        return None
    else:
        return x/y

EAFP ( è E più semplice rispetto a A sk F rispetto a P ermissione ):

def safe_divide_2(x, y):
    try:
        return x/y
    except ZeroDivisionError:  
        print "Divide-by-0 attempt detected"
        return None

La mia domanda è questa: non avevo mai sentito parlare dell'utilizzo di EAFP come costrutto di validazione dei dati primari, proveniente da un background Java e C ++. EAFP è qualcosa che è saggio usare in Java? O c'è troppo overhead dalle eccezioni? So che c'è un sovraccarico solo quando viene effettivamente generata un'eccezione, quindi non sono sicuro del perché non venga utilizzato il metodo più semplice di EAFP. È solo una preferenza?

È stato utile?

Soluzione

Personalmente, e penso che questo sia supportato da una convenzione, l'EAFP non è mai un buon modo di procedere. Puoi vederlo come equivalente al seguente:

if (o != null)
    o.doSomething();
else
    // handle

al contrario di:

try {
    o.doSomething()
}
catch (NullPointerException npe) { 
    // handle
}

Inoltre, considera quanto segue:

if (a != null)
    if (b != null)
        if (c != null)
            a.getB().getC().doSomething();
        else
            // handle c null
    else
        // handle b null
else
    // handle a null

Questo può sembrare molto meno elegante (e sì, questo è un esempio rozzo - abbi pazienza con me), ma ti dà una granularità molto maggiore nella gestione dell'errore, invece di avvolgerlo tutto in un tentativo di prenderlo NullPointerException , quindi cerca di capire dove e perché l'hai preso.

Il modo in cui lo vedo EAFP non dovrebbe mai essere usato, tranne per le rare situazioni. Inoltre, poiché hai sollevato il problema: sì, il blocco try-catch comporta un certo sovraccarico anche se l'eccezione non viene generata.

Altri suggerimenti

Se stai accedendo ai file, EAFP è più affidabile di LBYL, perché le operazioni coinvolte in LBYL non sono atomiche e il file system potrebbe cambiare tra il tempo che cerchi e il tempo che salti. In realtà, il nome standard è TOCTOU - Time of Check, Time of Use; i bug causati da un controllo impreciso sono bug TOCTOU.

Valuta la possibilità di creare un file temporaneo che deve avere un nome univoco. Il modo migliore per scoprire se esiste ancora il nome del file scelto è provare a crearlo - assicurandoti di utilizzare le opzioni per assicurarti che l'operazione non riesca se il file esiste già (in termini POSIX / Unix, il flag O_EXCL su open () ). Se provi a verificare se il file esiste già (probabilmente utilizzando access () ), allora tra il momento in cui viene visualizzato il messaggio " No " e quando provi a creare il file, qualcuno o qualcos'altro potrebbe aver creato il file.

Supponi di provare a leggere un file esistente. Il tuo controllo dell'esistenza del file (LBYL) potrebbe indicare "è lì", ma quando lo apri effettivamente, trovi "non è lì".

In entrambi questi casi, è necessario controllare l'operazione finale e LBYL non ha aiutato automaticamente.

(Se stai scherzando con i programmi SUID o SGID, access () pone una domanda diversa; potrebbe essere rilevante per LBYL, ma il codice deve ancora tenere conto della possibilità di errore. )

Oltre al costo relativo delle eccezioni in Python e Java, tieni presente che c'è una differenza nella filosofia / atteggiamento tra di loro. Java cerca di essere molto severo riguardo ai tipi (e tutto il resto), richiedendo dichiarazioni esplicite e dettagliate delle firme di classe / metodo. Si presume che dovresti sapere, in qualsiasi momento, esattamente quale tipo di oggetto stai usando e cosa è in grado di fare. Al contrario, la "digitazione anatra" di Python " significa che non sai con certezza (e non ti dovrebbe interessare) quale sia il tipo manifest di un oggetto, devi solo preoccuparti che cuci quando lo chiedi. In questo tipo di ambiente permissivo, l'unico atteggiamento sano è presumere che le cose funzioneranno, ma essere pronti ad affrontare le conseguenze se non lo fanno. La naturale limitazione di Java non si adatta bene a un approccio così casuale. (Questo non ha lo scopo di denigrare né l'approccio né la lingua, ma piuttosto dire che questi atteggiamenti fanno parte del linguaggio di ogni lingua e che copiare idiomi tra lingue diverse può spesso portare a imbarazzo e scarsa comunicazione ...)

Le eccezioni sono gestite in modo più efficiente in Python che in Java, il che è almeno in parte il motivo per cui vedi quel costrutto in Python. In Java, è più inefficiente (in termini di prestazioni) utilizzare le eccezioni in questo modo.

Considera questi frammenti di codice:

def int_or_default(x, default=0):
    if x.isdigit():
        return int(x)
    else:
        return default

def int_or_default(x, default=0):
    try:
        return int(x)
    except ValueError:
        return default

Entrambi sembrano corretti, giusto? Ma uno di loro non lo è.

Il primo, usando LBYL, fallisce a causa di una sottile distinzione tra isdigit e isdecimal ; quando viene chiamato con la stringa " & # 9312; & # 178; & # 179; & # 127237; & # 8325; " ;, genererà un errore anziché restituire correttamente il valore predefinito.

In seguito, utilizzando EAFTP, si ottiene una gestione corretta, per definizione. Non c'è spazio per una discrepanza comportamentale, perché il codice che richiede il requisito è il codice che afferma tale requisito.

Usare LBYL significa prendere la logica interna e copiarla in ogni call-site. Invece di avere una codifica canonica delle tue esigenze, hai la possibilità gratuita di sbagliare ogni volta che chiami la funzione.

Vale la pena notare che EAFTP non è sulle eccezioni, e in particolare il codice Java non dovrebbe usare eccezioni pervasivamente. Si tratta di dare il lavoro giusto al giusto blocco di codice. Ad esempio, l'utilizzo dei valori di ritorno Opzionale è un modo perfettamente valido per scrivere il codice EAFTP ed è molto più efficace per garantire la correttezza rispetto a LBYL.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top