Risposte:
In Python, l'oggetto "null" è il singleton None
.
Il modo migliore per controllare le cose per "Noneness" è usare l'operatore di identità is
:
if foo is None:
...
None
, Null di Python?Non c'è null
in Python, invece c'è None
. Come già affermato, il modo più accurato per verificare che qualcosa sia stato dato None
come valore è utilizzare l' is
operatore di identità, che verifica che due variabili si riferiscano allo stesso oggetto.
>>> foo is None
True
>>> foo = 'bar'
>>> foo is None
False
None
None
è la sola istanza della classe NoneType
e ogni ulteriore tentativo di istanziare quella classe restituirà lo stesso oggetto, il che rende None
un singleton. I nuovi arrivati in Python vedono spesso messaggi di errore che menzionano NoneType
e si chiedono di cosa si tratti. È mia opinione personale che questi messaggi possano semplicemente menzionare None
per nome perché, come vedremo tra poco, None
lascia poco spazio all'ambiguità. Quindi se vedi qualche TypeError
messaggio che menziona che NoneType
non può farlo o non puoi farlo, sappi solo che è semplicemente quello None
che veniva usato in un modo che non può.
Inoltre, None
è una costante incorporata, non appena avvii Python è disponibile per l'uso da qualsiasi luogo, sia in modulo, classe o funzione. NoneType
al contrario no, dovresti prima ottenere un riferimento ad esso eseguendo None
una query per la sua classe.
>>> NoneType
NameError: name 'NoneType' is not defined
>>> type(None)
NoneType
Puoi verificare None
l'unicità con la funzione di identità di Python id()
. Restituisce il numero univoco assegnato a un oggetto, ogni oggetto ne ha uno. Se l'id di due variabili è lo stesso, indicano in realtà lo stesso oggetto.
>>> NoneType = type(None)
>>> id(None)
10748000
>>> my_none = NoneType()
>>> id(my_none)
10748000
>>> another_none = NoneType()
>>> id(another_none)
10748000
>>> def function_that_does_nothing(): pass
>>> return_value = function_that_does_nothing()
>>> id(return_value)
10748000
None
non può essere sovrascrittoNella versione molto più vecchia di Python (prima della 2.4) era possibile riassegnare None
, ma non più. Nemmeno come attributo di classe o nei confini di una funzione.
# In Python 2.7
>>> class SomeClass(object):
... def my_fnc(self):
... self.None = 'foo'
SyntaxError: cannot assign to None
>>> def my_fnc():
None = 'foo'
SyntaxError: cannot assign to None
# In Python 3.5
>>> class SomeClass:
... def my_fnc(self):
... self.None = 'foo'
SyntaxError: invalid syntax
>>> def my_fnc():
None = 'foo'
SyntaxError: cannot assign to keyword
È quindi sicuro presumere che tutti i None
riferimenti siano uguali. Non c'è "personalizzato" None
.
None
uso is
dell'operatoreQuando scrivi il codice potresti essere tentato di provare Noneness in questo modo:
if value==None:
pass
O per verificare la presenza di falsità come questa
if not value:
pass
Devi capire le implicazioni e perché spesso è una buona idea essere espliciti.
None
perché farlo
value is None
piuttosto che
value==None
Il primo equivale a:
id(value)==id(None)
Considerando che l'espressione value==None
è infatti applicata in questo modo
value.__eq__(None)
se il valore è davvero None
, otterrai quello che ti aspettavi.
>>> nothing = function_that_does_nothing()
>>> nothing.__eq__(None)
True
Nella maggior parte dei casi il risultato sarà lo stesso, ma il __eq__()
metodo apre una porta che annulla qualsiasi garanzia di accuratezza, poiché può essere ignorato in una classe per fornire un comportamento speciale.
Considera questa classe.
>>> class Empty(object):
... def __eq__(self, other):
... return not other
Quindi lo provi None
e funziona
>>> empty = Empty()
>>> empty==None
True
Ma poi funziona anche sulla stringa vuota
>>> empty==''
True
E ancora
>>> ''==None
False
>>> empty is None
False
None
come booleanoI seguenti due test
if value:
# do something
if not value:
# do something
sono infatti valutati come
if bool(value):
# do something
if not bool(value):
# do something
None
è un "falso", il che significa che se lanciato su un valore booleano ritornerà False
e se applicato l' not
operatore tornerà True
. Si noti tuttavia che non è una proprietà unica per None
. Oltre a False
se stesso, la proprietà è condivisa da liste vuote, tuple, set, dadi, stringhe, nonché da 0, e da tutti gli oggetti delle classi che implementano il __bool__()
metodo magico per restituire False
.
>>> bool(None)
False
>>> not None
True
>>> bool([])
False
>>> not []
True
>>> class MyFalsey(object):
... def __bool__(self):
... return False
>>> f = MyFalsey()
>>> bool(f)
False
>>> not f
True
Pertanto, quando si esegue il test delle variabili nel modo seguente, prestare particolare attenzione a ciò che si sta includendo o escludendo dal test:
def some_function(value=None):
if not value:
value = init_value()
In quanto sopra, intendevi chiamare init_value()
quando il valore è impostato in modo specifico su None
, oppure intendevi che un valore impostato su 0
, o la stringa vuota o un elenco vuoto dovrebbe attivare anche l'inizializzazione. Come ho detto, sii consapevole. Come spesso accade in Python esplicito è meglio che implicito .
None
in praticaNone
utilizzato come valore del segnaleNone
ha uno status speciale in Python. È un valore di base preferito perché molti algoritmi lo trattano come un valore eccezionale. In tali scenari può essere utilizzato come flag per segnalare che una condizione richiede una gestione speciale (come l'impostazione di un valore predefinito).
È possibile assegnare None
agli argomenti della parola chiave di una funzione e quindi testarlo esplicitamente.
def my_function(value, param=None):
if param is None:
# do something outrageous!
È possibile restituirlo come predefinito quando si cerca di ottenere l'attributo di un oggetto e quindi testarlo esplicitamente prima di fare qualcosa di speciale.
value = getattr(some_obj, 'some_attribute', None)
if value is None:
# do something spectacular!
Per impostazione predefinita, il get()
metodo di un dizionario ritorna None
quando si tenta di accedere a una chiave inesistente:
>>> some_dict = {}
>>> value = some_dict.get('foo')
>>> value is None
True
Se dovessi provare ad accedervi usando la notazione in pedice a KeyError
verrebbe sollevato
>>> value = some_dict['foo']
KeyError: 'foo'
Allo stesso modo se si tenta di far apparire un oggetto inesistente
>>> value = some_dict.pop('foo')
KeyError: 'foo'
che è possibile eliminare con un valore predefinito generalmente impostato su None
value = some_dict.pop('foo', None)
if value is None:
# booom!
None
usato sia come flag che come valore validoGli usi sopra descritti si None
applicano quando non è considerato un valore valido, ma più come un segnale per fare qualcosa di speciale. Esistono tuttavia situazioni in cui a volte è importante sapere da dove None
provengono perché anche se viene utilizzato come segnale potrebbe anche essere parte dei dati.
Quando si interroga un oggetto per il suo attributo con il getattr(some_obj, 'attribute_name', None)
recupero, None
non viene indicato se l'attributo a cui si stava tentando di accedere era impostato None
o se era del tutto assente dall'oggetto. Stessa situazione quando si accede a una chiave da un dizionario come some_dict.get('some_key')
, non si sa se some_dict['some_key']
manca o se è appena impostato su None
. Se hai bisogno di tali informazioni, il solito modo di gestirle è tentare direttamente di accedere all'attributo o alla chiave all'interno di un try/except
costrutto:
try:
# equivalent to getattr() without specifying a default
# value = getattr(some_obj, 'some_attribute')
value = some_obj.some_attribute
# now you handle `None` the data here
if value is None:
# do something here because the attribute was set to None
except AttributeError:
# we're now hanling the exceptional situation from here.
# We could assign None as a default value if required.
value = None
# In addition, since we now know that some_obj doesn't have the
# attribute 'some_attribute' we could do something about that.
log_something(some_obj)
Allo stesso modo con dict:
try:
value = some_dict['some_key']
if value is None:
# do something here because 'some_key' is set to None
except KeyError:
# set a default
value = None
# and do something because 'some_key' was missing
# from the dict.
log_something(some_dict)
I due esempi precedenti mostrano come gestire i casi di oggetto e dizionario, che dire delle funzioni? Stessa cosa, ma usiamo l'argomento della doppia parola chiave asterischi a tal fine:
def my_function(**kwargs):
try:
value = kwargs['some_key']
if value is None:
# do something because 'some_key' is explicitly
# set to None
except KeyError:
# we assign the default
value = None
# and since it's not coming from the caller.
log_something('did not receive "some_key"')
None
usato solo come valore validoSe scopri che il tuo codice è disseminato del try/except
modello sopra semplicemente per differenziare tra None
flag e None
dati, usa semplicemente un altro valore di test. Esiste un modello in cui un valore che non rientra nel set di valori validi viene inserito come parte dei dati in una struttura di dati e viene utilizzato per controllare e testare condizioni speciali (ad es. Confini, stato, ecc.). Tale valore è chiamato sentinella e può essere utilizzato come modo None
viene utilizzato come segnale. È banale creare una sentinella in Python.
undefined = object()
L' undefined
oggetto sopra è unico e non fa molto di tutto ciò che potrebbe interessare un programma, è quindi un eccellente sostituto di None
come bandiera. Si applicano alcuni avvertimenti, ulteriori informazioni dopo il codice.
Con la funzione
def my_function(value, param1=undefined, param2=undefined):
if param1 is undefined:
# we know nothing was passed to it, not even None
log_something('param1 was missing')
param1 = None
if param2 is undefined:
# we got nothing here either
log_something('param2 was missing')
param2 = None
Con dict
value = some_dict.get('some_key', undefined)
if value is None:
log_something("'some_key' was set to None")
if value is undefined:
# we know that the dict didn't have 'some_key'
log_something("'some_key' was not set at all")
value = None
Con un oggetto
value = getattr(obj, 'some_attribute', undefined)
if value is None:
log_something("'obj.some_attribute' was set to None")
if value is undefined:
# we know that there's no obj.some_attribute
log_something("no 'some_attribute' set on obj")
value = None
Come ho già detto, le sentinelle personalizzate vengono fornite con alcuni avvertimenti. Innanzitutto, non sono parole chiave come None
, quindi Python non le protegge. Puoi sovrascrivere il tuo undefined
sopra in qualsiasi momento, ovunque nel modulo sia definito, quindi fai attenzione a come li esponi e li usi. Successivamente, l'istanza restituita da object()
non è un singleton, se si effettua quella chiamata 10 volte si ottengono 10 oggetti diversi. Infine, l'uso di una sentinella è altamente idiosincratico. Una sentinella è specifica della libreria in cui viene utilizzata e come tale il suo ambito dovrebbe generalmente essere limitato agli interni della libreria. Non dovrebbe "fuoriuscire". Il codice esterno dovrebbe prenderne conoscenza solo se il loro scopo è di estendere o integrare l'API della libreria.
Non si chiama null come in altre lingue, ma None
. Esiste sempre solo un'istanza di questo oggetto, quindi puoi verificare l'equivalenza con x is None
(confronto delle identità) anziché x == None
, se lo desideri.
None
. Quindi NoneType
trionferanno tutte quelle persone intelligenti che hanno confrontato contro !
In Python, per rappresentare l'assenza di un valore, è possibile utilizzare il valore None ( types.NoneType.None ) per gli oggetti e "" (o len () == 0 ) per le stringhe. Perciò:
if yourObject is None: # if yourObject == None:
...
if yourString == "": # if yourString.len() == 0:
...
Per quanto riguarda la differenza tra "==" e "is", il test per l'identità dell'oggetto utilizzando "==" dovrebbe essere sufficiente. Tuttavia, poiché l'operazione "is" è definita come operazione di identità dell'oggetto, è probabilmente più corretto utilizzarla, anziché "==". Non sono sicuro che ci sia anche una differenza di velocità.
Ad ogni modo, puoi dare un'occhiata a:
0 == false
restituito true
è perché l'operatore double-equal digita la coercizione. Con l'operatore triple-egals, tuttavia, 0 === false
restituisce false
, poiché "numero" e "booleano" sono tipi diversi.
true
in condizionali. In Rust and Go, 0
e false
sono diversi tipi che non possono essere confrontati per l'uguaglianza.
Null è un tipo di oggetto speciale come:
>>>type(None)
<class 'NoneType'>
Puoi verificare se un oggetto è nella classe 'NoneType':
>>>variable = None
>>>variable is None
True
Ulteriori informazioni sono disponibili su Python Docs
Per il test del valore di Verità , "Nessuno" verifica direttamente come FALSO, quindi sarà sufficiente l'espressione più semplice:
if not foo:
True
per qualsiasi valore falsy, e non solo None
.
egg is None
sopraegg == None
: Quest'ultimo può essere sovraccaricato, e rischia di rompersi quando si confrontano oggetto valido con Nessuno (dipende da come è implementato, ma che non ti aspetti a tutti di prendere comparazioni con nessuno in considerazione, vero?) , mentreis
funziona sempre allo stesso modo.