如何检查列表是否为空?

例如,如果通过以下内容:

a = []

如何检查a是否为空?

答案

if not a:
  print("List is empty")

使用空list隐式布尔值是非常 Python 的。

这样做的 pythonic 方法来自PEP 8 样式指南 (其中Yes表示 “推荐”, No表示 “不推荐”):

对于序列(字符串,列表,元组),请使用空序列为假的事实。

<b>Yes:</b> if not seq:
     if seq:

<b>No:</b>  if len(seq):
     if not len(seq):

我明确喜欢它:

if len(li) == 0:
    print('the list is empty')

这样,就可以 100%清楚地知道li是一个序列(列表),我们要测试其大小。 if not li: ...我的问题是if not li: ...会给人一种错误的印象,即li是一个布尔变量。

这是 google 首次针对 “python 测试空数组” 和类似的查询,再加上其他人似乎在推广问题,不仅仅是列表,所以我想为很多人添加另一种类型的序列的警告可能会用。

其他方法不适用于 NumPy 数组

您需要注意 NumPy 数组,因为其他适用于list或其他标准容器的方法对 NumPy 数组均无效。我在下面解释了原因,但总之, 首选方法是使用size

“pythonic” 方式无效:第 1 部分

NumPy 数组的 “pythonic” 方法失败,因为 NumPy 尝试将数组转换为bool的数组,并且if x尝试一次为某种聚合真值求值所有这些bool s。但这没有任何意义,因此您会遇到ValueError

>>> x = numpy.array([0,1])
>>> if x: print("x")
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

“pythonic” 方式无效:第 2 部分

但是至少上述情况告诉您它失败了。如果您碰巧有一个仅包含一个元素的 NumPy 数组, if在没有错误的意义上, if语句将 “起作用”。但是,如果一个元素恰好是0 (或0.0False ,...),则if语句将错误地导致False

>>> x = numpy.array([0,])
>>> if x: print("x")
... else: print("No x")
No x

但是显然x存在并且不为空!这个结果不是您想要的。

使用len可能会产生意想不到的结果

例如,

len( numpy.zeros((1,0)) )

即使数组有零个元素,也返回 1。

numpythonic 方式

SciPy 常见问题解答中所述,在您知道拥有 NumPy 数组的所有情况下,正确的方法是使用if x.size

>>> x = numpy.array([0,1])
>>> if x.size: print("x")
x

>>> x = numpy.array([0,])
>>> if x.size: print("x")
... else: print("No x")
x

>>> x = numpy.zeros((1,0))
>>> if x.size: print("x")
... else: print("No x")
No x

如果不确定是list ,NumPy 数组还是其他类型,可以将此方法与@dubiousjim 给出的答案结合使用以确保对每种类型使用正确的测试。并不是很 “pythonic”,但事实证明,NumPy 至少在这种意义上有意破坏了 pythonicity。

如果你需要做的不仅仅是检查,如果输入的是空的,而你正在使用其他的功能 NumPy 的像索引或数学运算,它可能是更有效的(当然更常见)来强制输入一个 NumPy 的阵列。有一些不错的函数可以快速完成此任务 - 最重要的是numpy.asarray 。这将接受您的输入,如果已经是数组,则不执行任何操作;如果是列表,元组等,则将您的输入包装到数组中,并有选择地将其转换为您选择的dtype 。因此,它可以在任何时候都非常快,并且可以确保您只是假设输入是 NumPy 数组。我们通常甚至只使用相同的名称,因为转换为数组不会使它返回当前范围之外

x = numpy.asarray(x, dtype=numpy.double)

这将使x.size检查在我在此页面上看到的所有情况下均有效。

检查列表是否为空的最佳方法

例如,如果通过以下内容:

a = []

如何检查 a 是否为空?

简短答案:

将列表放在布尔上下文中(例如,使用ifwhile语句)。如果为空,它将测试False ,否则为True 。例如:

if not a:                           # do this!
    print('a is an empty list')

PEP 8

PEP 8是 Python 标准库中 Python 代码的官方 Python 样式指南,它断言:

对于序列(字符串,列表,元组),请使用空序列为假的事实。

Yes: if not seq:
     if seq:

No: if len(seq):
    if not len(seq):

我们应该期望标准库代码应尽可能地具有高性能和正确性。但是为什么会这样,为什么我们需要此指南?

说明

我经常从 Python 的新手那里看到这样的代码:

if len(a) == 0:                     # Don't do this!
    print('a is an empty list')

懒惰语言的用户可能会这样做:

if a == []:                         # Don't do this!
    print('a is an empty list')

这些在其各自的其他语言中都是正确的。在 Python 中,这甚至在语义上都是正确的。

但是我们认为它不是 Python 语言,因为 Python 通过布尔强制转换直接在列表对象的界面中支持这些语义。

文档中 (并特别注意包含空列表[] ):

缺省情况下,对象被认为是真实的,除非它的类或者定义__bool__()方法,该方法返回False__len__()方法返回零,当与对象调用。以下是大多数被认为是错误的内置对象:

  • 定义为 false 的常量: NoneFalse
  • 任何数值类型的零: 00.00jDecimal(0) Fraction(0, 1)
  • 空序列和集合: ''()[]{}set()range(0)

以及数据模型文档:

object.__bool__(self)

调用以实现真值测试和内置操作bool() ;应该返回FalseTrue 。如果未定义此方法,则调用__len__() (如果已定义),并且如果其结果为非零,则认为该对象为 true。如果一个类__len__()__bool__() ,则其所有实例均被视为 true。

object.__len__(self)

调用以实现内置函数len() 。应该返回对象的长度,即 > = 0 的整数。而且,在布尔上下文中, __len__() __bool__()方法且其__len__()方法返回零的对象也被视为 false。

所以代替这个:

if len(a) == 0:                     # Don't do this!
    print('a is an empty list')

或这个:

if a == []:                     # Don't do this!
    print('a is an empty list')

做这个:

if not a:
    print('a is an empty list')

做 Pythonic 通常可以提高性能:

它还清吗? (请注意,执行等效操作的时间越少越好:)

>>> import timeit
>>> min(timeit.repeat(lambda: len([]) == 0, repeat=100))
0.13775854044661884
>>> min(timeit.repeat(lambda: [] == [], repeat=100))
0.0984637276455409
>>> min(timeit.repeat(lambda: not [], repeat=100))
0.07878462291455435

对于规模而言,这是调用函数以及构造和返回空列表的成本,您可以从上面使用的空度检查的成本中减去这些成本:

>>> min(timeit.repeat(lambda: [], repeat=100))
0.07074015751817342

我们看到,与内建函数长度或者检查len0 检查对空列表比使用语言的内置语法记载高性能的得多

为什么?

对于len(a) == 0检查:

首先,Python 必须检查全局变量,以查看len是否被遮盖。

然后,它必须调用该函数,加载0 ,并在 Python 中(而不是使用 C)进行相等比较:

>>> import dis
>>> dis.dis(lambda: len([]) == 0)
  1           0 LOAD_GLOBAL              0 (len)
              2 BUILD_LIST               0
              4 CALL_FUNCTION            1
              6 LOAD_CONST               1 (0)
              8 COMPARE_OP               2 (==)
             10 RETURN_VALUE

对于[] == []它必须建立一个不必要的列表,然后再次在 Python 的虚拟机(而不是 C)中执行比较操作。

>>> dis.dis(lambda: [] == [])
  1           0 BUILD_LIST               0
              2 BUILD_LIST               0
              4 COMPARE_OP               2 (==)
              6 RETURN_VALUE

因为列表的长度被缓存在对象实例头中,所以 “Pythonic” 方式是一种更简单,更快速的检查:

>>> dis.dis(lambda: not [])
  1           0 BUILD_LIST               0
              2 UNARY_NOT
              4 RETURN_VALUE

来自 C 源代码和文档的证据

PyVarObject

这是PyObject的扩展,添加了ob_size字段。这仅用于具有长度概念的对象。此类型通常不会出现在 Python / C API 中。它对应于通过扩展PyObject_VAR_HEAD宏定义的字段。

Include / listobject.h 中的 c 源:

typedef struct {
    PyObject_VAR_HEAD
    /* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */
    PyObject **ob_item;

    /* ob_item contains space for 'allocated' elements.  The number
     * currently in use is ob_size.
     * Invariants:
     *     0 <= ob_size <= allocated
     *     len(list) == ob_size

对评论的回应:

我要指出的是,对于非空情况也是如此,尽管它非常丑陋,如l=[]然后%timeit len(l) != 0 90.6 ns±8.3 ns, %timeit l != [] 55.6 ns ±3.09, %timeit not not l 38.5 ns±0.372。但是,尽管速度提高了三倍,但没有任何人not not l享受not not l 。看起来很荒谬。但是速度胜出
我想问题是用 timeit 进行测试,因为if l:是否足够,但令人惊讶的是%timeit bool(l) 101 ns±2.64 ns。有趣的是,没有这种惩罚就没有办法胁迫。 %timeit l没有用,因为不会进行任何转换。

IPython 的%timeit魔术在这里并非完全没有用:

In [1]: l = []                                                                  

In [2]: %timeit l                                                               
20 ns ± 0.155 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)

In [3]: %timeit not l                                                           
24.4 ns ± 1.58 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [4]: %timeit not not l                                                       
30.1 ns ± 2.16 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

我们可以看到,每增加一个线性成本,这里就not 。我们希望看到成本ceteris paribus ,也就是说, 其他所有条件都相等 - 尽可能将其他所有条件最小化:

In [5]: %timeit if l: pass                                                      
22.6 ns ± 0.963 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [6]: %timeit if not l: pass                                                  
24.4 ns ± 0.796 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [7]: %timeit if not not l: pass                                              
23.4 ns ± 0.793 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

现在让我们看一看一个空列表的情况:

In [8]: l = [1]                                                                 

In [9]: %timeit if l: pass                                                      
23.7 ns ± 1.06 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [10]: %timeit if not l: pass                                                 
23.6 ns ± 1.64 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [11]: %timeit if not not l: pass                                             
26.3 ns ± 1 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

我们可以在这里看到的是,无论是将实际的bool传递给条件检查还是将列表本身传递给您,都没有什么区别,并且如果有的话,按原样提供列表会更快。

Python 是用 C 编写的;它在 C 级别使用其逻辑。您用 Python 编写的任何内容都会变慢。除非您直接使用 Python 内置的机制,否则这可能会慢几个数量级。

空列表本身在真实值测试中被认为是错误的(请参阅python 文档 ):

a = []
if a:
     print "not empty"

@达伦 · 托马斯

编辑:反对测试空列表为假的另一点:多态性怎么样?您不应该依赖列表作为列表。它应该像鸭子一样嘎嘎叫 - 当它没有元素时,如何使 duckCollection 嘎嘎叫 “False”?

您的 duckCollection 应该实现__nonzero____len__因此 if a:可以正常工作。

帕特里克(已接受)的答案是正确的: if not a:则是正确的方法。 Harley Holcombe 的答案是正确的,因为这在 PEP 8 样式指南中。但是,答案没有一个能解释的是为什么遵循这个习惯用法是一个好主意 - 即使您个人发现它对于 Ruby 用户或其他任何人来说都不足够明确或令人困惑。

Python 代码和 Python 社区都有非常强大的习惯用法。遵循这些惯用法可以使您的代码更容易为有 Python 经验的人阅读。当您违反这些习惯用法时,这是一个强烈的信号。

的确, if not a:不会将空列表与None ,数字 0 或空元组,用户创建的空集合类型,用户创建的非相当集合类型或单元素 NumPy 数组区分开充当带有虚假值等的标量。有时,明确这一点很重要。而在这种情况下,你知道你想明确一下,这样你就可以测试究竟什么 。例如, if not a and a is not None:表示 “除 None 外所有虚假的东西”,而if len(a) != 0:表示 “仅空序列 - 除序列外的任何东西在这里都是错误”,依此类推。除了精确测试要测试的内容外,这还向读者表明该测试很重要。

但是,当您没有任何要明确的内容时,除了if not a:以外的其他任何内容都会误导读者。当您不重要时,您是在发出信号。 (您可能还会使代码的灵活性降低,变慢,变慢等等,但这并不那么重要。)而且,如果您习惯性地这样误导读者,那么当您确实需要区分时,它会被忽略,因为您在代码中一直在 “狼吞虎咽”。

为什么要检查?

似乎没有人已经解决了质疑你需要测试在首位名单。因为没有提供其他上下文,所以我可以想象您可能不需要首先进行此检查,但是您不熟悉 Python 中的列表处理。

我认为最 Python 的方式是根本不检查,而只是处理列表。这样,无论是空还是满,它都会做正确的事情。

a = []

for item in a:
    <do something with item>

<rest of code>

这具有处理任何内容,而无需对空虚特定检查的好处。如果a为空,则将不执行从属块,并且解释器将进入下一行。

如果确实需要检查数组是否为空,则其他答案就足够了。

len()用于 Python 列表,字符串,字典和集合的 O(1)操作 。 Python 在内部跟踪这些容器中元素的数量。

JavaScript 有一个 true / falsy 的类似概念

我写过:

if isinstance(a, (list, some, other, types, i, accept)) and not a:
    do_stuff

被投票为 - 1。我不确定这是否是因为读者反对该策略或认为答案对所提供的内容没有帮助。我会假装是后者,因为 - 不管什么都算是 “pythonic”- 这都是正确的策略。除非您已经排除或准备处理aFalse (例如False情况,否则您需要的测试要比if not a:更严格。您可以使用如下形式:

if isinstance(a, numpy.ndarray) and not a.size:
    do_stuff
elif isinstance(a, collections.Sized) and not a:
    do_stuff

第一次测试是针对上面 @Mike 的回答。第三行也可以替换为:

elif isinstance(a, (list, tuple)) and not a:

如果您只想接受特定类型(及其子类型)的实例,或者使用:

elif isinstance(a, (list, tuple)) and not len(a):

您无需进行显式的类型检查就可以逃脱,但前提是周围的上下文已经确保您确定a是您准备处理的类型的值,或者确定您不准备处理的类型是将会引发您准备处理的错误(例如,如果对未定义的值调用len TypeError )。通常,“pythonic” 约定似乎走到了最后。像鸭子一样挤压它,如果它不知道如何发出嘎嘎声,则让它引发 DuckError。但是,您仍然必须考虑要进行哪种类型的假设,以及您是否没有准备好正确处理的情况是否会在正确的地方出错。 Numpy 数组是一个很好的例子,仅盲目地依赖len或布尔类型转换可能无法完全满足您的期望。