Perché "if not someobj:" è meglio di "if someobj == None:" in Python?


127

Ho visto diversi esempi di codice come questo:

if not someobj:
    #do something

Ma mi chiedo perché non farlo:

if someobj == None:
    #do something

C'è qualche differenza? Uno ha un vantaggio rispetto all'altro?


13
Generalmente "someobj è Nessuno" è preferibile a "someobj == Nessuno"
Aaron Maenpaa,

che dire di pseudocodice se X non è None? Oppure X != None?
Charlie Parker

questa domanda è più dettagliata degli altri ... complimenti ...
alamin

Risposte:


187

Nel primo test, Python prova a convertire l'oggetto in un boolvalore se non lo è già. In parole povere, chiediamo l'oggetto: hai senso o no? Questo viene fatto utilizzando il seguente algoritmo:

  1. Se l'oggetto ha un __nonzero__metodo speciale (come i built-in numerici inte float), chiama questo metodo. Deve restituire un boolvalore che viene quindi utilizzato direttamente o un intvalore considerato Falsese uguale a zero.

  2. Altrimenti, se l'oggetto ha un __len__metodo speciale (come fanno container built-in, list, dict, set, tuple, ...), chiama questo metodo, si considera un contenitore Falsese vuota (lunghezza è zero).

  3. Altrimenti, l'oggetto viene considerato a Truemeno che non sia Nonenel qual caso, viene considerato False.

Nel secondo test, l'oggetto viene confrontato per uguaglianza None. Qui, stiamo chiedendo all'oggetto: "Sei uguale a questo altro valore?" Questo viene fatto utilizzando il seguente algoritmo:

  1. Se l'oggetto ha un __eq__metodo, viene chiamato e il valore restituito viene quindi convertito in un boolvalore e utilizzato per determinare il risultato di if.

  2. Altrimenti, se l'oggetto ha un __cmp__metodo, viene chiamato. Questa funzione deve restituire intun'indicazione dell'ordine dei due oggetti ( -1if self < other, 0if self == other, +1if self > other).

  3. Altrimenti, l'oggetto viene confrontato per identità (cioè sono riferimento allo stesso oggetto, come può essere testato isdall'operatore).

È possibile eseguire un altro test utilizzando l' isoperatore. Vorremmo chiedere all'oggetto: "Sei questo particolare oggetto?"

In generale, consiglierei di utilizzare il primo test con valori non numerici, di utilizzare il test per l'uguaglianza quando si desidera confrontare oggetti della stessa natura (due stringhe, due numeri, ...) e verificare l'identità solo quando usando valori sentinella ( Nonesignificato non inizializzato per un campo membro per esempio, o quando si usano getattri __getitem__metodi o ).

Riassumendo, abbiamo:

>>> class A(object):
...    def __repr__(self):
...        return 'A()'
...    def __nonzero__(self):
...        return False

>>> class B(object):
...    def __repr__(self):
...        return 'B()'
...    def __len__(self):
...        return 0

>>> class C(object):
...    def __repr__(self):
...        return 'C()'
...    def __cmp__(self, other):
...        return 0

>>> class D(object):
...    def __repr__(self):
...        return 'D()'
...    def __eq__(self, other):
...        return True

>>> for obj in ['', (), [], {}, 0, 0., A(), B(), C(), D(), None]:
...     print '%4s: bool(obj) -> %5s, obj == None -> %5s, obj is None -> %5s' % \
...         (repr(obj), bool(obj), obj == None, obj is None)
  '': bool(obj) -> False, obj == None -> False, obj is None -> False
  (): bool(obj) -> False, obj == None -> False, obj is None -> False
  []: bool(obj) -> False, obj == None -> False, obj is None -> False
  {}: bool(obj) -> False, obj == None -> False, obj is None -> False
   0: bool(obj) -> False, obj == None -> False, obj is None -> False
 0.0: bool(obj) -> False, obj == None -> False, obj is None -> False
 A(): bool(obj) -> False, obj == None -> False, obj is None -> False
 B(): bool(obj) -> False, obj == None -> False, obj is None -> False
 C(): bool(obj) ->  True, obj == None ->  True, obj is None -> False
 D(): bool(obj) ->  True, obj == None ->  True, obj is None -> False
None: bool(obj) -> False, obj == None ->  True, obj is None ->  True

4
Sebbene tecnicamente corretto, questo non spiega che tuple, liste, dicts, strs, unicodes, ints, float, ecc . Abbiano un valore diverso da zero . È molto più comune fare affidamento sul valore di verità del tipo incorporato piuttosto che fare affidamento su un metodo diverso da zero personalizzato .
ddaa,

50

Queste sono in realtà entrambe cattive pratiche. C'era una volta, era considerato OK trattare casualmente None e False come simili. Tuttavia, dal momento che Python 2.2 questa non è la migliore politica.

In primo luogo, quando si fa un if xo if not xtipo di test, Python deve convertire implicitamente xa booleani. Le regole per la boolfunzione descrivono una zattera di cose false; tutto il resto è vero. Se il valore di x non era correttamente booleano all'inizio, questa conversione implicita non è davvero il modo più chiaro per dire le cose.

Prima di Python 2.2 non esisteva la funzione bool, quindi era ancora meno chiaro.

Secondo, non dovresti davvero provare con == None. Dovresti usare is Nonee is not None.

Vedi PEP 8, Guida allo stile per Python Code .

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

  Also, beware of writing "if x" when you really mean "if x is not None"
  -- e.g. when testing whether a variable or argument that defaults to
  None was set to some other value.  The other value might have a type
  (such as a container) that could be false in a boolean context!

Quanti sono i singoli? Cinque: None, True, False, NotImplementede Ellipsis. Dal momento che è molto improbabile che tu usi NotImplementedo Ellipsis, e non lo diresti mai if x is True(perché semplicemente if xè molto più chiaro), lo testerai sempre e solo None.


3
La seconda forma è categoricamente non cattiva pratica. PEP 8 consiglia di utilizzare if x due volte . Prima per le sequenze (invece di usare len) e poi per True e False (invece di usare is). Praticamente tutto il codice Python che ho visto usa se x e se non x.
Antti Rasinen,

35

Perché Nonenon è l'unica cosa considerata falsa.

if not False:
    print "False is false."
if not 0:
    print "0 is false."
if not []:
    print "An empty list is false."
if not ():
    print "An empty tuple is false."
if not {}:
    print "An empty dict is false."
if not "":
    print "An empty string is false."

False, 0, (), [], {}E ""sono tutte diverse None, in modo che le due frammenti di codice sono non equivalenti.

Inoltre, considera quanto segue:

>>> False == 0
True
>>> False == ()
False

if object:non è un controllo di uguaglianza. 0, (), [], None, {}, Ecc sono tutte diverse le une dalle altre, ma tutte valutare False.

Questa è la "magia" dietro espressioni di corto circuito come:

foo = bar and spam or eggs

che è una scorciatoia per:

if bar:
    foo = spam
else:
    foo = eggs

anche se dovresti davvero scrivere:

foo = spam if bar else egg

Per quanto riguarda la tua ultima domanda, sono equivalenti.
Sylvain Defresne,

Ed entrambi errati perché "" è falso. Il secondo dovrebbe leggere '("",) o ("s",)'. Comunque, le versioni moderne di Python hanno un vero operatore ternario. Questo hack soggetto a errori dovrebbe essere bandito.
ddaa,

4

PEP 8 - Guida allo stile per il codice Python raccomanda di utilizzo è o non è se si sta testando per None-ness

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

D'altra parte, se si sta eseguendo il test per un valore superiore a Assente, è necessario utilizzare l'operatore booleano.


3

Se chiedi

if not spam:
    print "Sorry. No SPAM."

viene chiamato il metodo __nonzero__ di spam . Dal manuale di Python:

__nonzero__ ( self ) Chiamato per implementare il test del valore di verità e l'operazione integrata bool (); dovrebbero restituire False o True o i loro equivalenti interi 0 o 1. Quando questo metodo non è definito, viene chiamato __len __ (), se definito (vedere di seguito). Se una classe non definisce né __len __ () né __nonzero __ (), tutte le sue istanze sono considerate vere.

Se chiedi

if spam == None:
    print "Sorry. No SPAM here either."

il metodo __eq__ di spam viene chiamato con l'argomento Nessuno .

Per maggiori informazioni sulle possibilità di personalizzazione, dai un'occhiata alla documentazione di Python su https://docs.python.org/reference/datamodel.html#basic-customization


2

Questi due confronti hanno scopi diversi. Il primo controlla il valore booleano di qualcosa, il secondo controlla l'identità con valore None.


1
Per essere assolutamente corretti, il secondo controlla -equality- con un valore None ... "someobj is None" controlla l'identità.
Matthew Trevor,

0

Per uno il primo esempio è più breve e sembra più bello. Come per gli altri post, ciò che scegli dipende anche da cosa vuoi veramente fare con il confronto.


0

La risposta è, dipende".

Uso il primo esempio se considero 0, "", [] e False (elenco non esaustivo) equivalenti a None in questo contesto.


0

Personalmente, ho scelto un approccio coerente tra le lingue: lo faccio if (var)(o equivalente) solo se var è dichiarato booleano (o definito come tale, in C non abbiamo un tipo specifico). Ho anche prefisso queste variabili con un b(quindi sarebbe bVareffettivamente) per essere sicuro che non userò accidentalmente un altro tipo qui.
Non mi piace molto il cast implicito in booleano, ancor meno quando ci sono numerose regole complesse.

Certo, le persone non saranno d'accordo. Alcuni vanno oltre, vedo if (bVar == true)nel codice Java sul mio lavoro (troppo ridondante per i miei gusti!), Altri amano una sintassi troppo compatta, andando while (line = getNextLine())(troppo ambiguo per me).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.