se A vs se A non è Nessuno:


154

Posso usare:

if A:

invece di

if A is not None:

Quest'ultimo sembra così prolisso. C'è una differenza?

Risposte:


149

La dichiarazione

if A:

chiamerà A.__nonzero__()(consultare la documentazione relativa ai nomi dei metodi speciali ) e utilizzerà il valore restituito di quella funzione. Ecco il riassunto:

object.__nonzero__(self)

Chiamato per implementare il test del valore di verità e l'operazione integrata bool(); dovrebbero restituire Falseo True, o i loro equivalenti interi 0o 1. Quando questo metodo non è definito, __len__()viene chiamato, se è definito, e l'oggetto viene considerato vero se il suo risultato è diverso da zero. Se una classe non definisce né __len__()__nonzero__(), tutte le sue istanze sono considerate vere.

D'altro canto,

if A is not None:

confronta solo il riferimento Acon Noneper vedere se è lo stesso o meno.


4
e così A is not Noneè più veloce poiché c'è molto meno lavoro da fare
John La Rooy,

40
@gnibbler Non secondo i miei test. if object(): passè ~ 0,130 usec per loop, mentre if object() is not None: pass~ 0,135 usec. Ad ogni modo, non dovresti usare le prestazioni per scegliere tra questi due, ma piuttosto guardare le differenze nel modo in cui funzionano, poiché non sono equivalenti .
Lauritz V. Thaulow,

1
@gnibbler if A is not Nonesembra essere più lento perché è un confronto e deve caricare il singleton incorporato Nonecome un passo intermedio con cui confrontare A(dai un'occhiata a dis.dis()). Correggimi se sbaglio ma if A:sembra essere più efficiente, non appena vuoi davvero testare il valore di verità e non l' Noneidentità.
Cedbeu,

2
@cedbeu, sembra dipendere dal valore di A. Ho provato ora python -m timeit -s"a=0" "if a: pass" "else: pass"è più veloce di python -m timeit -s"a=0" "if a is None: pass" "else: pass"ma python -m timeit -s"a=1" "if a: pass" "else: pass"è più lento. Potrebbe dipendere dalla piattaforma, vedi se ottieni gli stessi risultati
John La Rooy

2
@cedbeu, in Python3 sono tutti molto più veloci, ma il is Nonetest è stato davvero il più lento per me. In pypy hanno tutti misurato esattamente lo stesso :)
John La Rooy

51

Come scritto in PEP8 :

  • Il confronto con singoli come Nessuno dovrebbe sempre essere fatto con 'is' o 'is', mai con gli operatori di uguaglianza .

    Inoltre, fai attenzione a scrivere "se x" quando intendi veramente "se x non è Nessuno" , ad esempio quando verifichi se una variabile o un argomento predefinito su Nessuno fosse impostato su un altro valore. L'altro valore potrebbe avere un tipo (come un contenitore) che potrebbe essere falso in un contesto booleano!


7
Mmmh, tuttavia, non è molto chiaro e non risponde all'OP. Il reindirizzamento automatico al PEP non è sempre la buona risposta. Molto spesso non si è interessati al test di identità con il singleton None, ma solo al controllo del valore di verità. In questo caso, if A:sembra più efficiente (prendi a dis.dis(), ci sono i passaggi extra per caricare l'integrato Nonee confrontarlo, con if A is not None:, mentre c'è solo un jump_ifnell'altro caso).
Cedbeu,

29
if x: #x is treated True except for all empty data types [],{},(),'',0 False, and None

quindi non è lo stesso di

if x is not None # which works only on None

17

Molte funzioni restituiscono None se non ci sono risultati appropriati. Ad esempio, il .first()metodo di una query SQLAlchemy restituisce None se non c'erano righe nel risultato. Supponiamo di aver selezionato un valore che potrebbe restituire 0 e che sia necessario sapere se è effettivamente 0 o se la query non ha prodotto alcun risultato.

Un linguaggio comune è dare all'argomento facoltativo di una funzione o di un metodo il valore predefinito Nessuno, e quindi verificare che quel valore sia Nessuno per vedere se è stato specificato. Per esempio:

def spam(eggs=None):
    if eggs is None:
        eggs = retrievefromconfigfile()

confrontalo con:

def spam(eggs=None):
    if not eggs:
        eggs = retrievefromconfigfile()

In quest'ultimo caso, cosa succede se chiami spam(0) o spam([])? La funzione rileverà (erroneamente) che non hai passato un valore per eggse calcolerebbe un valore predefinito per te. Probabilmente non è quello che vuoi.

Oppure immagina un metodo come "restituisci l'elenco delle transazioni per un determinato account". Se l'account non esiste, potrebbe restituire Nessuno. Ciò è diverso dalla restituzione di un elenco vuoto (il che significherebbe "questo account esiste ma non ha registrato transazioni).

Infine, torniamo al database. C'è una grande differenza tra NULL e una stringa vuota. Una stringa vuota in genere dice "qui c'è un valore e quel valore non è niente". NULL dice "questo valore non è stato inserito."

In ognuno di questi casi, ti consigliamo di utilizzare if A is None. Stai verificando un valore specifico - Nessuno - non solo "qualsiasi valore che viene trasmesso a False".


10

Fanno cose molto diverse .

I controlli qui sotto se A ha nulla se non i valori False, [], None, ''e 0. Verifica il valore di A.

if A:

Quanto segue controlla se A è un oggetto diverso da Nessuno. Verifica e confronta il riferimento (indirizzo di memoria) di A e Nessuno.

if A is not None:

AGGIORNAMENTO: ulteriore spiegazione

Molte volte i due sembrano fare la stessa cosa, quindi molte persone li usano in modo intercambiabile - è una pessima idea. Il motivo per cui i due danno gli stessi risultati è molte volte per pura coincidenza a causa di ottimizzazioni dell'interprete / compilatore come lo stage o qualcos'altro.

Tenendo presenti queste ottimizzazioni, numeri interi e stringhe dello stesso valore finiscono per utilizzare lo stesso spazio di memoria. Questo probabilmente spiega perché due stringhe separate si comportano come se fossero uguali.

> a = 'test'
> b = 'test'
> a is b
True
> a == b
True

Altre cose non si comportano allo stesso modo però ...

> a = []
> b = []
> a is b
False
> a == b
True

Le due liste hanno chiaramente la loro memoria. Le tuple sorprendentemente si comportano come stringhe.

> a = ()
> b = ()
> a is b
True
> a == b
True

Probabilmente questo è perché le tuple sono garantite per non cambiare, quindi ha senso riutilizzare la stessa memoria.

In conclusione, non si può fare affidamento su coincidenze . Solo perché cigola come un'anatra non significa che sia un'anatra. Usa ise ==dipende da cosa vuoi veramente controllare. Queste cose possono essere difficili da eseguire il debug poiché si islegge come in prosa che spesso ci sfioriamo.


Noneè un singleton, non è un dettaglio di implementazione (a differenza di int o string interning). Non sono sicuro di seguire ciò che intendi.
Lev Levitsky,

@LevLevitsky il mio punto non è che Nonesi comporti in modo diverso rispetto into a strcausa di internato. Il mio punto è che ise ==stanno controllando cose diverse; controlla innanzitutto un indirizzo di memoria, il secondo controlla il contenuto degli indirizzi di memoria.
Pithikos,

@LevLevitsky Ho modificato la mia risposta ora per renderla un po 'più digeribile.
Pithikos,


6

La maggior parte delle guide che ho visto suggeriscono che dovresti usare

se una:

a meno che tu non abbia un motivo per essere più specifico.

Ci sono alcune lievi differenze. Esistono valori diversi da None che restituiscono False, ad esempio elenchi vuoti o 0, quindi pensa a cosa stai realmente testando.


5

Nessuno è un valore speciale in Python che di solito designa una variabile non inizializzata. Per verificare se A non ha questo valore particolare che usi:

if A is not None

I valori di Falsey sono una classe speciale di oggetti in Python (ad esempio false, []). Per verificare se A è falso usare:

if not A

Pertanto, le due espressioni non sono uguali E faresti meglio a non trattarle come sinonimi.


PS Nessuno è anche falso, quindi la prima espressione implica la seconda. Ma il secondo copre altri valori di falso oltre a Nessuno. Ora ... se puoi essere sicuro di non poter avere altri valori di falso oltre a Nessuno in A, puoi sostituire la prima espressione con la seconda.


Non è che ti sbagli, ma questa risposta copre già questo.
Makoto,

boh, vorrei chiederti di rimuovere il downvote se non ti dispiace. la mia risposta è equivalente a tutte le altre, forse meno con quella a cui ti riferisci, ma è presentata da una prospettiva diversa e potrebbe essere più semplice da capire per qualcuno. inoltre, quando vedo un downvote su SO presumo una risposta sbagliata ... che ammetti non è il caso.
mircealungu,

Bene, questa ipotesi è incompleta. Potrebbe essere sbagliato o semplicemente non utile. Poiché la tua risposta, come ho affermato, era già coperta, non sono convinto che sia utile.
Makoto,

4

Dipende dal contesto.

Uso if A:quando mi aspetto Adi essere una sorta di raccolta e voglio eseguire il blocco solo se la raccolta non è vuota. Ciò consente al chiamante di passare qualsiasi raccolta ben educata, vuota o no, e di fare ciò che mi aspetto. Permette anche NoneeFalse sopprime l'esecuzione del blocco, che a volte è conveniente per chiamare il codice.

OTOH, se mi aspetto Adi essere un oggetto completamente arbitrario, ma avrebbe potuto essere predefinitoNone , quindi lo uso sempreif A is not None , poiché il codice chiamante potrebbe aver deliberatamente passato un riferimento a una raccolta vuota, una stringa vuota o un tipo numerico con valore 0, oppure booleano Falseo qualche istanza di classe che risulta essere falsa in un contesto booleano.

D'altra parte, se mi aspetto Adi essere qualcosa di più specifico (ad esempio istanza di una classe che chiamerò metodi di), ma avrebbe potuto essere Nonepredefinito e considero la conversione booleana predefinita come un proprietà della classe che non mi dispiace far rispettare su tutte le sottoclassi, quindi userò solo if A:per salvare le dita il terribile fardello di scrivere altri 12 caratteri.


3

Ho creato un file chiamato test.pyed eseguito sull'interprete. Puoi cambiare ciò che vuoi, per verificare con certezza come stanno le cose dietro le quinte.

import dis

def func1():

    matchesIterator = None

    if matchesIterator:

        print( "On if." );

def func2():

    matchesIterator = None

    if matchesIterator is not None:

        print( "On if." );

print( "\nFunction 1" );
dis.dis(func1)

print( "\nFunction 2" );
dis.dis(func2)

Questa è la differenza dell'assemblatore:

Fonte:

>>> import importlib
>>> reload( test )

Function 1
  6           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

  8           6 LOAD_FAST                0 (matchesIterator)
              9 POP_JUMP_IF_FALSE       20

 10          12 LOAD_CONST               1 ('On if.')
             15 PRINT_ITEM
             16 PRINT_NEWLINE
             17 JUMP_FORWARD             0 (to 20)
        >>   20 LOAD_CONST               0 (None)
             23 RETURN_VALUE

Function 2
 14           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

 16           6 LOAD_FAST                0 (matchesIterator)
              9 LOAD_CONST               0 (None)
             12 COMPARE_OP               9 (is not)
             15 POP_JUMP_IF_FALSE       26

 18          18 LOAD_CONST               1 ('On if.')
             21 PRINT_ITEM
             22 PRINT_NEWLINE
             23 JUMP_FORWARD             0 (to 26)
        >>   26 LOAD_CONST               0 (None)
             29 RETURN_VALUE
<module 'test' from 'test.py'>

2

Il primo è più Pythonic (miglior codice ideomatico), ma non eseguirà il blocco se A è False (non None).


7
-1. Come menziona @klesh, PEP8 dice di usare is/is not None. Seguire PEP8 è Pythonic. Inoltre i due test sono diversi .
JCotton,

1

python> = 2.6,

se scriviamo come

if A:

genererà un avviso come,

FutureWarning: il comportamento di questo metodo cambierà nelle versioni future. Utilizzare invece il test specifico "len (elem)" o "elem non è nessuno".

Quindi possiamo usare

if A is not None:
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.