2013-04-05 16 views
8

ho refactoring del codice piuttosto crufty e sono imbattuto nel seguente costrutto piuttosto strana:if (foo o bar o baz) è Nessuno:

#!/usr/bin/env python2.7 
# ... 
if (opts.foo or opts.bar or opts.baz) is None: 
    # (actual option names changed to protect the guilty) 
    sys.stderr.write("Some error messages that these are required arguments") 

... e mi chiedevo se questo sarebbe mai dare un senso immaginabile.

ho cambiato in qualcosa di simile a:

#!/usr/bin/env python2.7 
if None in (opts.foo, opts.bar, opts.baz): 
    # ... 

ho fuoco di un interprete ed effettivamente provare il primo costrutto ... sembra solo a lavorare se i valori sono tutte false e l'ultimo dei questi valori falsi sono Nessuno. (In altre parole, l'implementazione di CPython sembra restituire il primo vero o l'ultimo valore falso da una catena di espressioni o).

ho ancora il sospetto che il codice corretto dovrebbe utilizzare il qualsiasi() o tutti() built-in che sono stati aggiunti 2.5 (il codice in questione richiede già 2.7). Non sono ancora sicuro di quale sia la semantica preferita/intenzionale poiché sto iniziando questo progetto.

Quindi c'è qualche caso in cui questo codice originale avrebbe senso?

+0

Questo è terribile. fai attenzione a non introdurre un nuovo bug risolvendolo :) –

risposta

5

Il comportamento di cortocircuito causa il foo or bar or baz per restituire il primo dei tre valori che è booleano-true o l'ultimo valore se tutti sono booleani-falsi. Quindi in pratica significa "se tutti sono falsi e l'ultimo è Nessuno".

La versione modificata è leggermente diversa. if None in (opts.foo, opts.bar, opts.baz) inserirà, ad esempio, il blocco if se opts.foo è Nessuno e gli altri due sono 1, mentre la versione originale no (poiché None or 1 or 1 verrà valutata a 1, che non è Nessuno). La versione entrerà nel if quando qualsiasi dei tre è Nessuno, indipendentemente da ciò che gli altri due sono, mentre la versione originale entrerà nel if solo se l'ultimo vale None e gli altri due sono qualsiasi boolean- valori falsi.

Quale delle due versioni che si desidera dipende da come il resto del codice è strutturato e quali valori le opzioni potrebbero prendere (in particolare, se essi potrebbero avere valori booleani-falsi diverso da Nessuno, come False o 0 o una stringa vuota). Intuitivamente la tua versione sembra più ragionevole, ma se il codice ha trucchi particolari come questo, non sai mai quali casi d'angolo potrebbero emergere.

5

Si comporta in questo modo perché or è un operatore di cortocircuito, i dettagli sono in docs. Così il vostro primo if economico è pari a:

if opts.baz is None 

Potremmo immaginare ciò che l'autore di tale codice previsto. Penso che, come hai detto, abbia pensato di usare not all([opts.foo, opts.bar, opts.baz]).

+1

Per rispondere alla sua domanda in modo più specifico: sembra che il codice sia in errore e non riesco a pensare a un caso in cui avrebbe senso. – Anorov

+1

Avrebbe senso se non ci fosse un operatore "è" lì. Ma questo è tecnicamente testato se tutti i valori valutano come falso * e * l'ultimo è, in particolare, un riferimento al singleton Nessuno. –

+0

@JimDennis Effettivamente – Anorov

0

Io preferirei

if any(i is None for i in (opts.foo, opts.bar, opts.baz)) 

quanto esprime esattamente l'obiettivo previsto.

OTOH,

not all([opts.foo, opts.bar, opts.baz]) 

non verificare la presenza di falsità, non per None.

Il codice originale non sembra avere senso; sembra che sia stato scritto da qualcuno ignaro di quello che stanno facendo.

0

proviamo entrambi due del codice:

In [20]: foo = True 

In [22]: bar = None 

In [23]: baz = None 

In [24]: foo or bar or baz 
Out[24]: True 

In [25]: (foo or bar or baz) is None 
Out[25]: False 

In [28]: ((foo or bar or baz) is None) == (None in (foo, bar, baz)) 
Out[28]: False 

potete vedere il vostro riscrittura non è uguale al codice originale.

Il tuo primo ritorno di circostanza vera solo quando tutte le variabili vale None

In [19]: (None or None or None) is None 
Out[19]: True 

in modo da poter riscrivere la prima condizione a:

if foo == bar == bar == None: 
+0

Ho riconosciuto che la semantica è diversa; il punto qui è di speculare su se ci sono casi plausibili per la semantica originale per essere corretta. Penso che il codice precedente avrebbe fallito in vari casi angolari che, fortunatamente, non sembrano mai essere stati notati nella pratica. (Fa parte di un'utilità di riga di comando interna che è stata utilizzata solo da pochissimi membri dello staff). –

Problemi correlati