2013-09-03 9 views
8

In scrivere del codice parsing XML, ho ricevuto l'avvertimento:Differenza tra se e se <obj><obj> non è None

FutureWarning: The behavior of this method will change in future versions. Use specific 'len(elem)' or 'elem is not None' test instead. 

dove ho usato if <elem>: per verificare se un valore è stato trovato per un dato elemento.

Qualcuno può approfondire la differenza tra if <obj>: rispetto a if <obj> is not None: e perché Python si preoccupa di quello che utilizzo?

Quasi sempre uso il primo perché è più corto e non è un doppio negativo, ma spesso lo vedo nel codice sorgente di altre persone.

+0

Nota: attualmente [ 'se :' è equivalente a 'se .has_children():'] (http: //hg.python .org/cpython/file/43f27e69bc29/Lib/xml/etree/ElementTree.py # l231) è molto diverso da 'se non è None:'. – jfs

risposta

8

if obj is not None verificare se l'oggetto non è Nessuno. if obj verifica se bool(obj) è True.

Ci sono molti oggetti che non sono Nessuno ma per cui bool(obj) è False: ad esempio, una lista vuota, un dict vuoto, un set vuoto, una stringa vuota. . .

Utilizzare if obj is not None quando si desidera verificare se un oggetto non è Nessuno. Utilizzare if obj solo se si desidera verificare la "falsità" generale, la cui definizione è dipendente dall'oggetto.

+1

Per riferimento, qui ci sono tutti i [valori falsi] (http://docs.python.org/2/library/stdtypes.html#truth-value-testing) –

+1

Gli oggetti restituiti da lxml etre 'find' si comportano in modo strano. Otterrai questo 'FutureWarning' per i nodi restituiti da, ad es. 'node = root.find ('.// ​​tag /')'. [Maggiori dettagli in questo articolo ho scritto] (http://yummycoding.tumblr.com/post/82299053082/lxml-and-futurewarning) ma gist è che 'node' sarebbe falso se non avesse figli, indipendentemente dal fatto che il nodo corrispondente al modello è stato trovato. Quindi 'if node' significa" Il nodo di corrispondenza esiste ** e ** la corrispondenza ha figli ". Ecco perché sei avvisato di essere esplicito: usa 'len (nodo)' per significare "Ha figli"; usa 'nodo non è None' per significare che" Nodo di corrispondenza esiste ". –

0

Il comportamento di if x è una sorta di interessante:

In [1]: def truthy(x): 
...:  if x: 
...:   return 'Truthy!' 
...:  else: 
...:   return 'Not truthy!' 
...:  

In [2]: truthy(True) 
Out[2]: 'Truthy!' 

In [3]: truthy(False) 
Out[3]: 'Not truthy!' 

In [4]: truthy(0) 
Out[4]: 'Not truthy!' 

In [5]: truthy(1) 
Out[5]: 'Truthy!' 

In [6]: truthy(None) 
Out[6]: 'Not truthy!' 

In [7]: truthy([]) 
Out[7]: 'Not truthy!' 

In [8]: truthy('') 
Out[8]: 'Not truthy!' 

Così, per esempio, le dichiarazioni sotto il condizionale if x saranno non eseguire se x è 0, None, la lista vuota, o una stringa vuota . D'altra parte if x is not None si applica solo quando x è esattamente None.

1

Per una spiegazione completa consideri il seguente esempio:

>>> import dis 
>>> def is_truthy(x): 
>>> return "Those sweed words!" if x else "All lies!" 
>>> is_truthy(None) 
'All lies!' 
>>> is_truthy(1) 
'Those sweed words!' 
>>> is_truthy([]) 
'All lies!' 
>>> is_truthy(object()) 
'Those sweed words!' 

Cosa succede a is_truthy()? Scopriamolo. Esecuzione dis.dis(is_truthy) ti dà:

2   0 LOAD_FAST    0 (x) 
       3 POP_JUMP_IF_FALSE  10 
       6 LOAD_CONST    1 ('The pure word') 
       9 RETURN_VALUE   
     >> 10 LOAD_CONST    2 ('All lies!') 
       13 RETURN_VALUE 

Come si può vedere x viene inserito nello stack, quindi POP_JUMP_IF_FALSE viene eseguito. Questo prenderà il salto prima spinta e poi restituirà la risposta giusta.

POP_JUMP_IF_FALSE è definito in ceval.c:

TARGET(POP_JUMP_IF_FALSE) { 
    PyObject *cond = POP(); 
    int err; 
    if (cond == Py_True) { 
     Py_DECREF(cond); 
     FAST_DISPATCH(); 
    } 
    if (cond == Py_False) { 
     Py_DECREF(cond); 
     JUMPTO(oparg); 
     FAST_DISPATCH(); 
    } 
    err = PyObject_IsTrue(cond); 
    Py_DECREF(cond); 
    if (err > 0) 
     err = 0; 
    else if (err == 0) 
     JUMPTO(oparg); 
    else 
     goto error; 
    DISPATCH(); 

Come si può vedere, se l'oggetto consumato da POP_JUMP_IF_FALSE è già sia True o False, la risposta è semplice. In caso contrario, l'interprete prova a scoprire se l'oggetto è vero chiamando il numero PyObject_IsTrue() definito nello object protocol. Il code in object.c vi mostra esattamente come funziona:

PyObject_IsTrue(PyObject *v) 
{ 
    Py_ssize_t res; 
    if (v == Py_True) 
     return 1; 
    if (v == Py_False) 
     return 0; 
    if (v == Py_None) 
     return 0; 
    else if (v->ob_type->tp_as_number != NULL && 
      v->ob_type->tp_as_number->nb_bool != NULL) 
     res = (*v->ob_type->tp_as_number->nb_bool)(v); 
    else if (v->ob_type->tp_as_mapping != NULL && 
      v->ob_type->tp_as_mapping->mp_length != NULL) 
     res = (*v->ob_type->tp_as_mapping->mp_length)(v); 
    else if (v->ob_type->tp_as_sequence != NULL && 
      v->ob_type->tp_as_sequence->sq_length != NULL) 
     res = (*v->ob_type->tp_as_sequence->sq_length)(v); 
    else 
     return 1; 
    /* if it is negative, it should be either -1 or -2 */ 
    return (res > 0) ? 1 : Py_SAFE_DOWNCAST(res, Py_ssize_t, int); 
} 

Anche in questo caso, se l'oggetto è solo True o False se stessi, la risposta è semplice. None è considerato falso.Quindi vengono controllati vari protocolli come number protocol, mapping protocol e sequence protocol. Altrimenti l'oggetto è considerato vero.

Per concludere: x si considera vero se è True, vero in base al numero, al protocollo di mappatura o sequenza o ad un altro tipo di oggetto. Se vuoi che il tuo oggetto venga valutato come falso, puoi farlo implementando uno qualsiasi dei suddetti protocolli, vedi i collegamenti forniti.

Il confronto con None come in if x is None è un confronto esplicito. La logica sopra non si applica.

+0

[I documenti definiscono il test del valore di verità] (http://docs.python.org/2/library/stdtypes.html#truth-value-testing) abbastanza chiaramente. – jfs

5

Questa risposta si rivolge specificamente a FutureWarning.

Quando lxml è stato scritto per la prima volta, lxml.etree._Element è stato considerato falso se non aveva figli.

Di conseguenza, questo può accadere:

>>> from lxml import etree 
>>> 
>>> root = etree.fromstring('<body><h1>Hello</h1></body>') 
>>> print root 
<Element body at 0x41d7680> 
>>> print "root is not Falsey" if root else "root is Falsey" 
<string>:1: FutureWarning: The behavior of this method will change in future versions. Use specific 'len(elem)' or 'elem is not None' test instead. 
root is not Falsey 
>>> # that's odd, a warning 
>>> h1 = root.find('.//h1') 
>>> print h1 
<Element h1 at 0x41d7878> 
>>> print "h1 is not Falsey" if h1 else "h1 is Falsey" 
h1 is Falsey 
>>> # huh, that is weird! In most of python, an object is rarely False 
>>> # we did see a warning though, didn't we? 
>>> # let's see how the different elements output 
>>> print "root is not None" if root is not None else "root is None" 
root is not None 
>>> print "h1 is not None" if h1 is not None else "h1 is None" 
h1 is not None 
>>> print "Length of root is ", len(root) 
Length of root is 1 
>>> print "Length of h1 is ", len(h1) 
Length of h1 is 0 
>>> # now to look for something that's not there! 
>>> h2 = root.find('.//h2') 
>>> print h2 
None 
>>> print "h2 is not Falsey" if h2 else "h2 is Falsey" 
h2 is Falsey 
>>> print "h2 is not None" if h2 is not None else "h2 is None" 
h2 is None 
>>> print "Length of h2 is ", len(h2) 
Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
TypeError: object of type 'NoneType' has no len() 
Length of h2 is >>> 

lxml ha promesso per più di 7 anni che questo cambiamento sta per accadere (dopo aver attraversato diverse versioni), ma non ha mai seguito attraverso sulla minaccia, senza dubbio a causa di quanto centrale lxml è e teme che si rompa un sacco di codice esistente.

Tuttavia, per essere sia esplicito e sicuro che non commette un errore, non usare mai if obj o if not obj se questo oggetto ha un tipo di lxml.etree._Element.

Invece, utilizzare uno dei seguenti controlli:

obj = root.find('.//tag') 

if obj is not None: 
    print "Object exists" 

if obj is None: 
    print "Object does not exist/was not found" 

if len(obj): # warning: if obj is None (no match found) TypeError 
    print "Object has children" 

if not len(obj): # warning: if obj is None (no match found) TypeError 
    print "Object does not have children" 
Problemi correlati