例如,如果通过以下步骤:
a = []
如何检查a是否为空?
例如,如果通过以下步骤:
a = []
如何检查a是否为空?
当前回答
检查列表是否为空的最佳方法例如,如果通过以下步骤:a=[]如何检查a是否为空?
简短回答:
将列表放在布尔上下文中(例如,使用if或while语句)。如果为空,则测试False,否则测试True。例如:
if not a: # do this!
print('a is an empty list')
第8段
PEP8是Python标准库中Python代码的官方Python风格指南,它断言:
对于序列(字符串、列表、元组),请使用空序列为false的事实。是:如果不是seq:如果seq:否:如果len(seq):如果不是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通过布尔强制在列表对象的接口中直接支持这些语义。
从文档中(并特别注意空列表的包含,[]):
默认情况下,除非对象的类定义返回False的__bool__()方法或__len__()方法当使用对象调用时,返回零。以下是大多数被认为是假的内置对象:常量定义为false:None和false。任何数字类型的零:0,0.0,0j,十进制(0),分数(0,1)空序列和集合:“”,(),[],{},set(),range(0)
以及数据模型文档:
对象__布尔_(自身)调用以实现真值测试和内置操作bool();应返回False或True。当未定义该方法时,__如果定义了len__(),则调用它,如果结果为非零,则该对象视为真。如果类既没有定义__len__()也不是__bool_(),它的所有实例都被认为是真的。
and
对象__len__(自己)调用以实现内置函数len()。应该返回对象的长度,一个大于等于0的整数。此外,未定义__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
对于scale,这里是调用函数、构造和返回空列表的成本,您可以从上面使用的空检查的成本中减去该成本:
>>> min(timeit.repeat(lambda: [], repeat=100))
0.07074015751817342
我们看到,使用内置函数len(与0相比)检查长度,或者检查空列表,都比使用所记录的语言的内置语法性能差得多。
Why?
对于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=[],然后%timeitlen(l)!=0 90.6纳秒±8.3纳秒,%时间l!=[]55.6 ns±3.09,%时间不超过38.5 ns±0.372。但是,尽管速度是原来的三倍,但没有人会喜欢我。这看起来很可笑。但速度取胜我想问题是用timeit测试,因为仅当l:足够,但令人惊讶的是%timeit bool(l)产生101 ns±2.64 ns。有趣的是,如果没有这种惩罚,就没有办法强迫发出嘘声。%时间是无用的,因为不会发生转换。
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)
我们可以看到,每增加一项都会有一点线性成本。我们希望看到成本相同,也就是说,所有其他方面都是平等的——所有其他方面尽可能最小化:
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)
我们在这里看到的是,无论是将实际布尔值传递给条件检查还是传递给列表本身,都没有什么不同,如果有什么不同的话,那么给出列表的速度会更快。
Python是用C编写的;它使用C级的逻辑。用Python编写的任何内容都会比较慢。除非您直接使用Python内置的机制,否则它可能会慢几个数量级。
其他回答
这是谷歌对“python测试空数组”和类似查询的第一次点击,其他人将这个问题概括为列表之外的问题,所以这里有很多人使用的不同类型的序列的警告。
其他方法不适用于NumPy数组
您需要小心使用NumPy数组,因为对于列表或其他标准容器工作正常的其他方法对于NumPy阵列失败。我在下面解释了原因,但简而言之,首选的方法是使用大小。
“蟒蛇”的方式行不通:第一部分
NumPy数组的“pythonic”方法失败,因为NumPy试图将数组强制转换为布尔值数组,如果x试图同时计算所有布尔值以获得某种聚合真值。但这没有任何意义,因此您会得到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()
“蟒蛇”的方式行不通:第2部分
但至少上述案例告诉你,它失败了。如果您恰好有一个仅包含一个元素的NumPy数组,则If语句将“有效”,即不会出现错误。但是,如果该元素恰好为0(或0.0,或False,…),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数组的情况下,正确的方法是在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
如果您不确定它是列表、NumPy数组还是其他类型,可以将此方法与@dubiusjim给出的答案结合起来,以确保对每种类型使用正确的测试。不是很“蟒蛇”,但事实证明NumPy至少在这个意义上故意破坏了蟒蛇。
如果您需要做的不仅仅是检查输入是否为空,而且您正在使用其他NumPy特性,如索引或数学运算,那么强制输入为NumPy数组可能更有效(当然也更常见)。有几个很好的函数可以快速实现这一点,最重要的是numpy.asarray。它接受您的输入,如果它已经是一个数组,则不执行任何操作,如果它是一个列表、元组等,则将您的输入包装到一个数组中,并可选地将其转换为您选择的数据类型。因此,无论何时,它都非常快速,并确保您只需假设输入是一个NumPy数组。我们通常甚至只使用相同的名称,因为转换到数组不会使其返回到当前范围之外:
x = numpy.asarray(x, dtype=numpy.double)
这将使x.size检查在我在本页上看到的所有情况下都有效。
方法1(首选):
if not a:
print ("Empty")
方法2:
if len(a) == 0:
print("Empty")
方法3:
if a == []:
print ("Empty")
在真值测试中,空列表本身被认为是错误的(请参见python文档):
a = []
if a:
print("not empty")
达伦·托马斯的回答:
编辑:反对测试的另一点空列表为False:多态性?你不应该依赖列表是列表。它应该只是像鸭子一样呱呱叫-你怎么样让你的duckCollection呱呱叫当它没有元素时为“False”?
duckCollection应该实现__nonzero_或__len__,因此if-a:将毫无问题地工作。
为什么要检查?
似乎没有人回答过你首先需要测试列表的问题。因为您没有提供额外的上下文,我可以想象,您可能不需要首先执行此检查,但不熟悉Python中的列表处理。
我认为,最Python的方法是根本不检查,而只是处理列表。这样,无论是空的还是满的,它都会做正确的事情。
a = []
for item in a:
# <Do something with item>
# <The rest of code>
这有利于处理的任何内容,同时不需要对空性进行特定检查。如果a为空,则依赖块将不执行,解释器将进入下一行。
如果确实需要检查数组是否为空:
a = []
if not a:
# <React to empty list>
# <The rest of code>
足够了。
您甚至可以尝试像这样使用bool()。虽然它的可读性较低,但它确实是一种简洁的方法。
a = [1,2,3];
print bool(a); # it will return True
a = [];
print bool(a); # it will return False
我喜欢这种方式,因为检查列表是否为空。
非常方便和有用。