12

在编写一些 XML 解析代码时,我收到了警告:

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>:有人可以详细说明vs之间的区别if <obj> is not None:以及为什么 Python 关心我使用哪个吗?

我几乎总是使用前者,因为它更短而且不是双重否定的,但经常在其他人的源代码中看到后者。

4

4 回答 4

17

if obj is not None测试对象是否不是None。 if obj测试是否bool(obj)为真。

有许多对象不是 None 而是bool(obj)False:例如,一个空列表、一个空 dict、一个空集、一个空字符串。. .

if obj is not None当你想测试一个对象是否不是 None 时使用。if obj仅当您想测试一般的“错误”时才使用——其定义取决于对象。

于 2013-09-03T02:26:16.047 回答
10

这个答案专门针对 FutureWarning。

首次编写 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  >>> 

7 多年来,lxml 一直承诺这种变化将会发生(在经历了几个版本之后),但从未兑现过威胁,这无疑是因为 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"
于 2014-07-22T17:20:18.710 回答
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_FALSEceval.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已经是Trueor False,则答案很简单。否则,解释器试图通过调用对象协议中定义的方法来确定对象是否真实object.c中的代码向您展示了它是如何工作的:PyObject_IsTrue()

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

同样,如果对象只是TrueFalse他们自己,答案很简单。None也被认为是假的。然后检查数字协议映射协议序列协议等各种协议。否则该对象被认为是真实的。

总结一下:x如果是则被认为是True真,根据数字、映射或序列协议或其他某种对象为真。如果您希望您的对象评估为假,您可以通过实现任何上述协议来实现,请参阅提供的链接。

Nonelike inif x is None进行比较是显式比较。上述逻辑不适用。

于 2013-09-03T06:36:33.433 回答
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!'

因此,例如,如果为 0、None、空列表或空字符串,则条件下的语句if x不会执行。x另一方面,仅在isif x is not None时才适用。xNone

于 2013-09-03T05:56:08.863 回答