2012-02-29 6 views
37

Penso di capirlo completamente, ma voglio solo assicurarmi che continui a vedere le persone dire MAI MAI provare contro True, False o None. Suggeriscono che le routine dovrebbero generare un errore piuttosto che restituire False o Nessuno. Ad ogni modo, ho molte situazioni in cui voglio semplicemente sapere se un flag è impostato o meno, quindi la mia funzione restituisce True o False. Ci sono altre situazioni in cui ho una funzione restituita Nessuna se non ci sono risultati utili. Dal mio pensiero, nessuno dei due è un problema fino a quando mi rendo conto che non avrei mai usare:Uso di True, False e None come valori di ritorno nelle funzioni python

if foo == True 
if foo == False 
if foo == None 

ed utilizzi:

if foo is True 
if foo is False 
if foo is None 

dal True, False, e nessuno sono tutti single e sarà sempre valutare il modo in cui mi aspetto quando utilizzo "is" piuttosto che "==". Ho sbagliato qui?

Sulla stessa falsariga, sarebbe più pitioso modificare le funzioni che a volte restituiscono None in modo che generino un errore? Supponiamo che abbia un metodo di istanza chiamato "get_attr()" che recupera un attributo da qualche file. Nel caso in cui trovi che l'attributo che ho richiesto non esiste è appropriato restituire None? Sarebbe meglio farli sollevare un errore e prenderlo in seguito?

risposta

62

Il consiglio non è che non bisogna mai usoTrue, False o None. È solo che non dovresti usare if x == True.

if x == True è stupido perché == è solo un operatore binario! Ha un valore di ritorno di True o False, a seconda che i suoi argomenti siano uguali o meno. E if condition procederà se condition è vero. Quindi, quando scrivi if x == True Python sta per prima valutare x == True, che diventerà True se x era True e False in caso contrario, e quindi procedere se il risultato è vero. Ma se ti aspetti che x sia True o False, perché non usare direttamente lo if x!

Allo stesso modo, x == False può essere sostituito in genere da not x.

In alcuni casi è possibile utilizzare x == True. Questo perché una condizione dell'istruzione if è "valutata nel contesto booleano" per verificare se è "verità" anziché eseguire il test esattamente su True. Ad esempio, stringhe, elenchi e dizionari non vuoti sono tutti considerati attendibili da un'istruzione if, così come valori numerici diversi da zero, ma nessuno di questi è uguale a True. Quindi, se si desidera verificare se un valore arbitrario è esattamente il valore True, non solo se è vero, quando si utilizza if x == True. Ma non vedo quasi mai un uso per questo. È così raro che se tu abbia bisogno di scrivere , vale la pena aggiungere un commento in modo che gli sviluppatori futuri (incluso possibilmente te stesso) non si limitino a supporre che lo == True sia superfluo e rimuoverlo.


L'utilizzo di x is True invece è in realtà peggiore. Non utilizzare mai is con tipi immutabili di base incorporati come booleani (True, False), numeri e stringhe. Il motivo è che per questi tipi ci interessano i valori , non identità. == verifica che i valori siano gli stessi per questi tipi, mentre is verifica sempre le identità.

Test identità piuttosto che valori è male perché un'implementazione potrebbe teoricamente costruire nuovi valori booleani, piuttosto che andare a cercare quelle esistenti, portando a voi avere due True valori che hanno lo stesso valore, ma sono memorizzati in diversi posti in memoria e hanno diversi identità. In pratica sono abbastanza sicuro che lo e il False vengano sempre riutilizzati dall'interprete Python, quindi questo non accadrà, ma questo è davvero un dettaglio di implementazione. Questo problema fa scattare sempre le persone con le stringhe, perché le stringhe brevi e le stringhe letterali che appaiono direttamente nell'origine del programma vengono riciclate da Python, quindi 'foo' is 'foo' restituisce sempre True. Ma è facile costruire la stessa stringa in 2 modi diversi e avere Python da loro identità diverse. Osservare quanto segue:

>>> stars1 = ''.join('*' for _ in xrange(100)) 
>>> stars2 = '*' * 100 
>>> stars1 is stars2 
False 
>>> stars1 == stars2 
True 

EDIT: Così si scopre che la parità di Python su booleani è un po 'inaspettato (almeno per me):

>>> True is 1 
False 
>>> True == 1 
True 
>>> True == 2 
False 
>>> False is 0 
False 
>>> False == 0 
True 
>>> False == 0.0 
True 

Il razionale per questo, come spiegato in the notes when bools were introduced in Python 2.3.5, è che il vecchio comportamento dell'utilizzo degli interi 1 e 0 per rappresentare True e False era buono, ma volevamo solo nomi più descrittivi per i numeri che intendevamo rappresentare i valori di verità.

Un modo per raggiungere quello sarebbe stato semplicemente avere True = 1 e False = 0 nei builtin; quindi 1 e True sarebbero davvero indistinguibili (incluso da is). Ma ciò significherebbe anche che una funzione che restituisce True mostrerebbe 1 nell'interprete interattivo, quindi ciò che è stato fatto invece è creare bool come sottotipo di int. L'unica cosa che è diversa su bool è str e repr; Le istanze bool hanno ancora gli stessi dati delle istanze int e continuano a confrontare l'uguaglianza allo stesso modo, quindi True == 1.

Quindi è sbagliato usare x is True quando x potrebbe essere stato fissato dal codice che si aspetta che "Vero è solo un altro modo di precisare 1", perché ci sono un sacco di modi per costruire i valori che sono uguali a True, ma non lo fanno avere la stessa identità come:

>>> a = 1L 
>>> b = 1L 
>>> c = 1 
>>> d = 1.0 
>>> a == True, b == True, c == True, d == True 
(True, True, True, True) 
>>> a is b, a is c, a is d, c is d 
(False, False, False, False) 

ed è sbagliato usare x == True quando x potrebbe essere un valore arbitrario Python e si vogliono solo sapere se è il valore booleano True. L'unica certezza che abbiamo è che usare semplicemente x è il migliore quando si vuole testare la "verità". Per fortuna di solito è tutto ciò che è richiesto, almeno nel codice che scrivo!

Un modo più sicuro sarebbe x == True and type(x) is bool. Ma sta diventando piuttosto prolisso per un caso piuttosto oscuro. Inoltre, non sembra molto Pythonic eseguendo un controllo esplicito dei tipi ... ma è proprio quello che stai facendo quando stai provando a testare con precisione True piuttosto che sincero; il modo di battitura delle anatre sarebbe accettare i valori di verità e consentire a qualsiasi classe definita dall'utente di dichiararsi sincera.

Se hai a che fare con questo concetto estremamente preciso di verità in cui non solo consideri le raccolte non vuote come vere, ma anche che non consideri che 1 sia vero, quindi usare semplicemente x is True è probabilmente ok, perché presumibilmente allora sai che x non proviene dal codice che considera 1 vero. Non credo che ci sia un modo puramente pitone per trovare un altro True che vive in un indirizzo di memoria diverso (anche se probabilmente potresti farlo da C), quindi questo non dovrebbe mai rompersi nonostante sia teoricamente la cosa "sbagliata" fare.

E io ero solito pensare che i booleani fossero semplici!

Fine Edit


Nel caso di None, tuttavia, il linguaggio è quello di utilizzare if x is None. In molte circostanze è possibile utilizzare if not x, perché None è un valore "falso" in un'istruzione if. Ma è meglio farlo solo se vuoi trattare tutti i valori falsi (tipi numerici a valore zero, raccolte vuote e None) allo stesso modo. Se si ha a che fare con un valore che può essere un altro valore o None per indicare "nessun valore" (ad esempio quando una funzione restituisce None in caso di errore), è molto meglio utilizzare if x is None in modo da non accidentalmente assumere la funzione fallì quando è successo e basta per restituire un elenco vuoto, oppure il numero 0.

mie argomentazioni per l'utilizzo di == piuttosto che is per i tipi di valore immutabili suggerisce che si dovrebbe usare if x == None piuttosto che if x is None. Tuttavia, nel caso di None Python garantisce esplicitamente che esiste esattamente uno None nell'intero universo e il normale codice idiomatico di Python utilizza is.


Per quanto riguarda se tornare None o rilanciare un'eccezione, dipende dal contesto.

Per qualcosa come il tuo esempio get_attr mi aspetterei che generi un'eccezione, perché la chiamerò come do_something_with(get_attr(file)).La normale aspettativa dei chiamanti è che otterranno il valore dell'attributo, e averli ottenere None e assumere che il valore dell'attributo è un pericolo molto peggiore di dimenticare di gestire l'eccezione quando si può effettivamente continuare se l'attributo non può Essere trovato. Inoltre, restituire None per indicare l'errore significa che None non è un valore valido per l'attributo. Questo può essere un problema in alcuni casi.

Per una funzione immaginaria come see_if_matching_file_exists, che forniamo un modello e controlla più punti per vedere se c'è una corrispondenza, potrebbe restituire una corrispondenza se trova uno o None in caso contrario. Ma in alternativa potrebbe restituire un elenco di corrispondenze; quindi nessuna corrispondenza è solo la lista vuota (che è anche "falsa", questa è una di quelle situazioni in cui dovrei semplicemente usare if x per vedere se ho ottenuto qualcosa indietro).

Quindi, quando si sceglie tra le eccezioni e None per indicare un errore, è necessario decidere se None è un valore previsto non di errore e quindi osservare le aspettative del codice che chiama la funzione. Se l'aspettativa "normale" è che sarà restituito un valore valido, e solo occasionalmente un chiamante sarà in grado di funzionare correttamente indipendentemente dal fatto che venga restituito un valore valido, è necessario utilizzare eccezioni per indicare un errore. Se sarà abbastanza comune che non ci sia un valore valido, quindi i chiamanti si aspettano di gestire entrambe le possibilità, quindi è possibile utilizzare None.

+0

Grazie per questa risposta. Ha chiarito una serie di idee sbagliate che ho avuto per un po 'e chiarito alcuni altri punti che erano un po' di nebbia. Avevo pensato che True e False fossero come None in quel pitone che garantiva che i loro id sarebbero sempre stati gli stessi. Purtroppo questo significa che ho un sacco di codice per andare a guardare (grep) attraverso ... – Vorticity

+2

@Vorticity: onestamente, Python probabilmente * garantisce * che i loro ID sono sempre gli stessi, e anche se non garantisce che sono sicuro al 99% che saranno sempre gli stessi nella pratica. Quindi non sarei costretto a modificare 'x is True' da tutto il tuo codice se hai cose migliori da fare (come scrivere un nuovo codice :)). Ma generalmente si vogliono usare solo prove 'is' con oggetti mutabili (o' None', che è l'eccezione che conferma la regola). – Ben

+0

Dopo averci pensato un po 'di più, ho una domanda. Come puoi dire la differenza tra una variabile che contiene 'int (13)' e una che contiene 'True'? È solo che normalmente non ti importa se la variabile è veramente "Vero" e solo che è verità? Immagino di chiederlo perché sto analizzando avanti e indietro tra un fastidioso formato di file che contiene "sì" e "no" dappertutto, ma quando nel mio codice preferirei molto trattare con 'True' e' False' quindi ho dovuto scrivere codice per passare da uno all'altro con il minimo sforzo. – Vorticity

13

Utilizzare if foo o if not foo, non è necessario né per == né per is.

Per il controllo rispetto a Nessuno, si consigliano is None e is not None. Questo ti permette di distinguerlo da False (o cose che valgono come False, come "" e []).

Se il valore get_attr deve restituire None dipende dal contesto. Potresti avere un attributo in cui il valore è None e non potresti farlo. Interpreterei None come "unset" e un KeyError significherebbe che la chiave non esiste nel file.

+0

Grazie per la risposta e per il suggerimento su "if foo". Credo di averlo sempre evitato per abitudine a causa di esattamente quello che lei sottolinea "è Nessuno". Non mi è mai passato per la testa che "se foo è vero" è troppo lungo. – Vorticity

1

È possibile controllare direttamente che la variabile contenga valore o non come if var o not var.

0

Nel caso della funzione fittizia getattr, se l'attributo richiesto deve sempre essere disponibile ma non viene generato un errore. Se l'attributo è facoltativo, restituire None.

7

Se la verifica della verità:

if foo 

per falso:

if not foo 

per nessuno:

if foo is None 

per non nessuno:

if foo is not None 

Per getattr() il comportamento corretto è non tornare None ma generare un errore AttributError invece - a meno che la classe è qualcosa di simile defaultdict

6

Per quanto riguarda se sollevare un'eccezione o tornare None: dipende dal caso d'uso. O può essere pitonico. Guarda la classe dict di python, ad esempio, gli hook x[y] in dict.__getitem__ e solleva uno KeyError se la chiave non è presente. Ma il metodo dict.get restituisce il secondo argomento (che è impostato per default su None) se la chiave non è presente. Sono entrambi utili.

La cosa più importante da considerare è quella di documentare tale comportamento nella docstring e assicurarsi che il metodo get_attr() esegua ciò che dice.

per rispondere alle vostre domande, utilizzare queste convenzioni:

if foo: 
    # for testing truthiness 

if not foo: 
    # for testing falsiness 

if foo is None: 
    # testing .. Noneliness ? 

if foo is not None: 
    # check explicitly avoids common bugs caused by empty sequences being false 

funzioni che restituiscono True o False dovrebbe probabilmente avere un nome che rende questo ovvio per migliorare la leggibilità del codice

def is_running_on_windows(): 
    return os.name == 'nt' 

In python3 voi possibile digitare "suggerimento":

>>> def is_running_on_windows() -> bool: 
...  return os.name == 'nt' 
... 
>>> is_running_on_windows.__annotations__ 
{'return': bool} 
0

per True, non Nessuno:

if foo: 

per falso, Nessuno:

if not foo: 
1

Negli esempi PEP 8(Style Guide for Python Code) document, ho visto che foo is None o foo is not None vengono usati al posto di o foo == Nonefoo != None. È inoltre consigliabile utilizzare if boolean_value in questo documento anziché if boolean_value == True o if boolean_value is True. Quindi penso che se questo è il modo ufficiale di Python, noi ragazzi di Python dovremmo andare anche in questo modo.

Saluti.

0

Una cosa è assicurarsi che nulla possa riassegnare la variabile. Se non è un booleano alla fine, fare affidamento sulla verità porterà a bug. La bellezza della programmazione condizionale in lingue digitate dinamicamente :).

Le seguenti stampe "no".

x = False 
if x: 
    print 'yes' 
else: 
    print 'no' 

Ora cambiamo x.

x = 'False' 

Ora le stampe economico "" perché la stringa è truthy.

if x: 
    print 'yes' 
else: 
    print 'no' 

Questa affermazione, però, le uscite in modo corretto "no".

if x == True: 
    print 'yes' 
else: 
    print 'no' 
Problemi correlati