2013-09-03 13 views
8

einig XML-Parsing-Code Beim Schreiben erhielt ich die Warnung:Unterschied zwischen wenn <obj> und wenn <obj> nicht None ist

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

wo ich if <elem>: verwendet um zu überprüfen, ob ein Wert für ein gegebenes Element gefunden wurde.

Kann jemand näher auf den Unterschied zwischen if <obj>: vs if <obj> is not None: und warum Python kümmert, was ich verwende?

Ich benutze fast immer das erstere, weil es kürzer und nicht ein doppelt negatives ist, aber oft das letztere im Quellcode anderer Menschen sehen.

+0

Hinweis: zur Zeit [ 'wenn :' entspricht 'wenn .has_children():'] (http: //hg.python .org/cpython/file/43f27e69bc29/Lib/xml/etree/ElementTree.py # l231) das ist ganz anders als 'if ist nicht None:'. – jfs

Antwort

8

if obj is not None testen, ob das Objekt nicht None ist. if obj testet, ob bool(obj) True ist.

Es gibt viele Objekte, die nicht None sind, aber für die bool(obj) False ist: zum Beispiel eine leere Liste, ein leeres Diktat, ein leerer Satz, ein leerer String. . .

Verwenden Sie if obj is not None, wenn Sie testen möchten, ob ein Objekt nicht None ist. Verwenden Sie if obj nur, wenn Sie auf allgemeine "Falschheit" testen wollen - deren Definition objektabhängig ist.

+1

Als Referenz hier sind alle [falsche Werte] (http://docs.python.org/2/library/stdtypes.html#truth-value-testing) –

+1

Objekte zurückgegeben von lxml etree 'find' verhalten sich seltsam. Sie erhalten dieses 'FutureWarning' für Knoten, die von z.B. 'node = root.find ('.// ​​Tag /')'. [Weitere Details in diesem Artikel, den ich geschrieben habe] (http://yummycoding.tumblr.com/post/82299053082/lxml-and-futurewarning) aber das Wichtigste ist, dass "Knoten" falsch wäre, wenn es keine Kinder hätte, ob oder nicht a Knoten, der mit dem Muster übereinstimmt, wurde gefunden. Also bedeutet "wenn Knoten" bedeutet "passender Knoten existiert ** und ** Übereinstimmung hat Kinder". Deshalb werden Sie gewarnt, explizit zu sein: Verwenden Sie 'len (node)', um "Hat Kinder" zu bedeuten; Verwenden Sie 'node is not None', um" Matching node exists "zu bezeichnen. –

0

Das Verhalten von if x ist irgendwie interessant:

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!' 

So zum Beispiel Aussagen im Sinne der bedingten if x werden nicht ausführen, wenn x 0, None, die leere Liste, oder die leere Zeichenkette . Auf der anderen Seite if x is not None wird nur gelten, wenn x ist genau None.

1

Eine ausführliche Erklärung betrachten das folgende Beispiel:

>>> 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!' 

Was in is_truthy() geschieht? Lass es uns herausfinden. Laufen dis.dis(is_truthy) gibt Ihnen:

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 

Wie Sie x sehen kann, ist auf den Stapel geschoben, dann wird POP_JUMP_IF_FALSE ausgeführt. Dies wird den Sprung zum ersten drücken und dann die richtige Antwort zurückgeben.

POP_JUMP_IF_FALSE in ceval.c definiert:

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(); 

Wie Sie sehen können, wenn das Objekt von POP_JUMP_IF_FALSE verbraucht bereits entweder True oder False ist, die Antwort ist einfach. Andernfalls versucht der Interpreter herauszufinden, ob das Objekt truthy ist, indem er PyObject_IsTrue() aufruft, das in object protocol definiert ist. Die code in object.c zeigt Ihnen genau, wie es funktioniert:

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); 
} 

Noch einmal, wenn das Objekt nur True oder False ist selbst, die Antwort ist einfach. None gilt auch als falsch.Dann werden verschiedene Protokolle wie die , die mapping protocol und die sequence protocol überprüft. Ansonsten gilt das Objekt als wahr.

Um es zu wickeln: x gilt als wahr, wenn es True ist, wahr nach der Nummer, Zuordnung oder Sequenzprotokoll oder eine andere Art von Objekt. Wenn Sie möchten, dass Ihr Objekt als falsch ausgewertet wird, können Sie dies tun, indem Sie eines der Protokolle implementieren, siehe die bereitgestellten Links.

Vergleichen mit None wie in if x is None ist ein expliziter Vergleich. Die obige Logik gilt nicht.

+0

[Die Dokumente definieren Wahrheitstest] (http://docs.python.org/2/library/stdtypes.html#truth-value-testing) ziemlich klar. – jfs

5

Diese Antwort bezieht sich speziell auf das FutureWarning.

Als lxml zum ersten Mal geschrieben wurde, wurde lxml.etree._Element als falsch angesehen, wenn es keine Kinder hatte.

Als Ergebnis kann das passieren:

>>> 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 für 7+ Jahre viel versprechend ist, dass diese Änderung (nachdem sie durch mehrere Versionen), aber noch nie gefolgt durch auf die Bedrohung passieren wird, Zweifellos wegen der zentralen Bedeutung von lxml und befürchtet, dass dadurch viel bestehender Code zerstört wird.

jedoch explizite und sicherzustellen, dass Sie nicht einen Fehler machen, if obj oder if not obj nie verwenden, wenn das Objekt eine Art lxml.etree._Element hat.

Verwenden Sie stattdessen eine der folgenden Prüfungen verwenden:

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" 
Verwandte Themen