【问题标题】:Difference between if <obj> and if <obj> is not Noneif <obj> 和 if <obj> is not None 之间的区别
【发布时间】:2013-09-06 03:35:34
【问题描述】:

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

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

我使用if &lt;elem&gt;: 来检查是否为给定元素找到了值。

有人可以详细说明if &lt;obj&gt;:if &lt;obj&gt; is not None: 之间的区别以及为什么 Python 关心我使用哪个?

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

【问题讨论】:

标签: python python-2.7 xml-parsing


【解决方案1】:

此答案专门针对 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  >>> 

lxml 7 多年来一直承诺这种变化将会发生(在经历了几个版本之后),但从未兑现过威胁,这无疑是因为 lxml 的中心地位,并且担心它会破坏很多现有代码。

但是,为了明确并确保您没有犯错,如果该对象的类型为 lxml.etree._Element,则切勿使用 if objif not obj

改为使用以下检查之一:

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"

【讨论】:

    【解决方案2】:

    要获得完整的解释,请考虑以下示例:

    >>> 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 消费的对象已经是TrueFalse,答案很简单。否则,解释器试图通过调用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);
    }
    

    同样,如果对象只是TrueFalse 本身,答案很简单。 None 也被认为是错误的。然后检查各种协议,如number protocolmapping protocolsequence protocol。否则对象被认为是真的。

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

    if x is None 中的None 比较是显式比较。上面的逻辑不适用。

    【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      • 供参考,这里是所有falsey values
      • 从 lxml etree find 返回的对象行为异常。你会得到这个FutureWarning 用于返回的节点,例如node = root.find('.//tag/')More details in this article I wrote 但要点是 node 如果没有子节点,无论是否找到与模式匹配的节点,它都是错误的。所以if node 表示“匹配节点存在并且匹配有子节点”。这就是为什么你被警告要明确的原因:使用len(node) 表示“有孩子”;使用node is not None 表示“匹配节点存在”。
      • 要添加到 @BurhanKhalid 的评论,这里是 Python 3 的 falsey values
      猜你喜欢
      • 2022-07-04
      • 2011-02-12
      • 2014-09-09
      • 1970-01-01
      • 1970-01-01
      • 2010-11-06
      • 1970-01-01
      • 1970-01-01
      • 2012-07-04
      相关资源
      最近更新 更多