为什么对空 list/dict 的断言失败?

Why an assertion on an empty list/dict fails?

assert {}

失败 AssertionError

导致这个问题的原因如下:

import xml.etree.ElementTree as ET

xml_element = ET.Element("tag")

assert xml_element

也因 AssertionError 而失败。

assert 评估 expression,我明白了。

还有;

if []:
    print("foo")

不会打印任何内容,因为列表为空 returns False 但是,

if [1]:
    print("foo")

会打印 foo.

我的问题是如何检查此行为的内部结构?

什么时候正常转换为true/false?

您真正要问的问题是“将列表或字典转换为 bool 时会发生什么?”。这是因为 if x:assert x 的行为与您编写 if bool(x):assert bool(x) 的行为相同。 (顺便说一句,在那些地方显式调用 bool() 是错误的形式,正是因为它是多余的。)

答案是列表和字典(以及字符串和其他容器)在转换为 boolTrue 如果它们 不为空 。相反,如果它们是 empty.

,则在转换为 bool 时它们是 False

经常使用的术语是 non-empty 容器是“真实的”,空容器是“虚假的”。

这有一个有趣的结果,即 bool("False") 计算为 True - 因为 "False" 是一个 non-empty 字符串!

除了容器之外,还有其他一些常见情况:如果数字 non-zero 则为真(即 270.5 为真,而 00.0 是错误的),值 None 是错误的。

如何查找其他 classes

how could I have checked the internals of this behavior ?

如上所述,任何足够像集合的东西在 non-empty 时都是真实的;它是否“像一个容器”的通常测试是您是否可以在其上成功调用 len(x),在这种情况下,只要 len(x) 是 non-zero.

,它就是真实的

即使 class 不是容器(或数字),它仍然有可能对 bool() 进行一些有意义的转换。事实上,您甚至可以通过定义 __bool__() 方法在您自己的 classes 上定义它。确定这一点的唯一方法是查看您正在使用的库的文档,而不是查看源代码。

如果库没有定义这样的转换,那么 bool(x) 将总是 return True(这是没有 [=31 的 classes 的默认值=] 或 __len__() 定义的方法)。有关更多信息,请参阅官方 Python 文档中的 Truth value testing

特别是 ElementTree

xml.etree.ElementTree 特别是不幸的是,事情有点混乱。 A 看起来有点像元素的容器:e[0] return 是第一个子元素,for child in e return 是所有子元素,len(e) returns 子元素的数量(这些都记录在 official docs 中)。因此,当至少有一个子元素时 bool(e) 恰好为真,这是完全合理的——这与 Python 的所有其余元素的工作方式相符。但是,正如 Tomerikoo 的答案在源代码中所示,虽然目前有效,但已被弃用,将来可能无法使用。文档也提到了这一点,但它非常隐蔽:

Caution: Elements with no subelements will test as False. This behavior will change in future versions. Use specific len(elem) or elem is None test instead.

下面的代码片段解释了这背后令人困惑的逻辑。背景是,maybe_child = e.find("child") 找到名称为 "child"e 的子元素,如果没有找到这样的元素,则为 None。但是 None 是错误的,因此很容易通过编写 if maybe_child: 来检查此搜索是否成功 - 但如果找到了子元素但本身没有子元素,那实际上将算作 False 。这是文档中的片段:

element = root.find('foo')

if not element:  # careful!
    print("element not found, or element has no subelements")

if element is None:
    print("element not found")

来自 ElementTree's source code(属于 class Element):

class Element:
[...]
    def __init__(self, tag, attrib={}, **extra):
        if not isinstance(attrib, dict):
            raise TypeError("attrib must be dict, not %s" % (
                attrib.__class__.__name__,))
        self.tag = tag
        self.attrib = {**attrib, **extra}
        self._children = []
[...]
    def __bool__(self):
        warnings.warn(
            "The behavior of this method will change in future versions.  "
            "Use specific 'len(elem)' or 'elem is not None' test instead.",
            FutureWarning, stacklevel=2
            )
        return len(self._children) != 0 # emulate old behaviour, for now

因为你刚刚初始化了一个新元素,它的_children是一个空列表(在__init__中可以看到),所以如果你明白为什么assert []失败了,你应该明白为什么 assert xml_element 失败 as-well.