2013-09-03 2 views
8

При написании некоторых XML разбора кода, я получил предупреждение:разница между ними, если <obj> и если <obj> не является Ни

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

где я использовал if <elem>:, чтобы проверить, если значение было установлено для данного элемента.

Может ли кто-нибудь уточнить разницу между if <obj>: против if <obj> is not None: и почему Python заботится о том, что я использую?

Я почти всегда использую первое, потому что оно короче и не является двойным отрицательным, но часто видит последнее в исходном коде других людей.

+0

Примечание: в настоящее время [ 'если :' эквивалентно 'если это .has_children():'] (Http: //hg.python .org/cpython/file/43f27e69bc29/Lib/xml/etree/ElementTree.py # l231), который сильно отличается от 'if не None:'. – jfs

ответ

8

if obj is not None проверить, нет ли объекта. if obj проверяет, действительно ли bool(obj).

Существует много объектов, которые не являются None, но для которых bool(obj) является False: например, пустой список, пустой dict, пустой набор, пустая строка. , ,

Использовать if obj is not None, если вы хотите проверить, нет ли объекта. Используйте if obj только в том случае, если вы хотите протестировать общую «фальшь», определение которой объектно-зависимо.

+1

Для справки, вот все [значения falsey] (http://docs.python.org/2/library/stdtypes.html#truth-value-testing) –

+1

Объекты, возвращенные из lxml etree 'find' ведут себя странно. Вы получите это 'FutureWarning' для узлов, возвращенных из, например. 'node = root.find ('.// ​​tag /')'. [Подробнее в этой статье я написал] (http://yummycoding.tumblr.com/post/82299053082/lxml-and-futurewarning), но суть в том, что 'node' был бы ложным, если бы у него не было детей, независимо от того, узел, соответствующий шаблону. Поэтому 'if node' означает« Matching node exists ** и ** match has children ». Вот почему вас предупреждают, что они явны: используйте 'len (node)' для обозначения «Имеет детей»; использование 'node is not None' означает« узел соответствия »существует. –

0

Поведение if x является своего рода интересно:

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

Так, например, заявления по условной if x будет не выполняются, если x является 0, None, пустой список, или пустая строка , С другой стороны, if x is not None применим только тогда, когда x - это точно None.

1

Для полного объяснения рассмотрим следующий пример:

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

Что происходит в is_truthy()? Давай выясним. Запуск dis.dis(is_truthy) дает:

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 

Как вы можете видеть x помещается в стек, а затем POP_JUMP_IF_FALSE выполняется. Это приведет к прыжку для первого нажатия, а затем вернет правильный ответ.

POP_JUMP_IF_FALSE определяется в 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(); 

Как вы можете видеть, если объект потребляемый POP_JUMP_IF_FALSE уже либо True или False, ответ прост. В противном случае интерпретатор пытается выяснить, является ли объект правдой по телефону PyObject_IsTrue(), который определен в object protocol. code in object.c точно показывает, как это работает:

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

Опять же, если объект просто True или False сами, ответ прост. None также считается ложным.Затем проверяются различные протоколы, такие как number protocol, mapping protocol и sequence protocol. В противном случае объект считается истинным.

Для его обертывания: x считается истинным, если он равен True, истинным в соответствии с номером, протоколом сопоставления или последовательности или каким-либо другим видом объекта. Если вы хотите, чтобы ваш объект оценивал значение false, вы можете сделать это, выполнив любой из указанных протоколов, см. Предоставленные ссылки.

По сравнению с None, как в if x is None, это явное сравнение. Приведенная выше логика не применяется.

+0

[Документы определяют тестирование значения истины] (http://docs.python.org/2/library/stdtypes.html#truth-value-testing) довольно четко. – jfs

5

В этом ответе конкретно рассматривается будущее.

Когда lxml был впервые написан, lxml.etree._Element считался ложным, если у него не было детей.

В результате, это может произойти:

>>> 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 был перспективен для 7+ лет, что это изменение произойдет (после прохождения через несколько версий), но никогда не следовал через об угрозе, без сомнения, из-за того, что центральный lxml есть и боится, что он сломает много существующего кода.

Однако, чтобы быть как явные, так и, что вы не сделаете ошибку, никогда не использовать if obj или if not obj если объект имеет тип lxml.etree._Element.

Вместо этого используйте один из следующих проверок:

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" 
Смежные вопросы