在python中,为了确认一个数组或者元组是不是为空,可以使用三种方法,(由于数组和元组在这里判断类似,因此统一以数组说明。)分别是与空数组比较,查看数组长度是否为零和直接if判断。

python实现方法如下

#! env python

## 判断数组为空的3种方法

###方法1:与空数组比较
def compare_empty(a):
    if a == []:
        return True
    return False

###方法2:判断数组长度是否为0
def compare_len(a):
    if len(a) == 0 :
        return True
    return False
###方法3:if直接判断
def list_if(a):
    if a:
        return False
    return True


一般情况下,我们推荐使用的方法是第三种方法,因为这是python内置的方法,运行起来会比较高效,那么为什么呢,python内部是如何实现的呢?

三种方法在python内部的实现原理

由于python是解释性语言,代码运行时首先会被转化为字节码,然后再由解释器来真实运行。为了确认python内部的实现过程,这里需要使用python里的dis包的dis方法查看代码生成的字节码。以下为3种不同方法的字节码查看。

与空数组比较

#! env python
import dis 
def compare_empty(a):
    if a == []:
        return True
    return False
dis.dis(compare_empty)
"""
In [7]: dis.dis(compare_empty)                                                  
  2           0 LOAD_FAST                0 (a)
              2 BUILD_LIST               0
              4 COMPARE_OP               2 (==)
              6 POP_JUMP_IF_FALSE       12

  3           8 LOAD_CONST               1 (True)
             10 RETURN_VALUE

  4     >>   12 LOAD_CONST               2 (False)
             14 RETURN_VALUE


"""

如果没有使用过dis的话看起来可能会比较麻烦,这里稍微解释下,左边的数字代表原始代码的行数,右边的代表改行被翻译成的字节码,一般来说一行python代码会对应多行字节码。由于不同的方法的差别仅仅是在第二行if判断语句,因此只看第二行翻译的字节码差异即可。

在该方法中,有4个步骤:

"""
  2           0 LOAD_FAST                0 (a)
              2 BUILD_LIST               0
              4 COMPARE_OP               2 (==)
              6 POP_JUMP_IF_FALSE       12
"""

分别为:

  1. LOAD_FAST 查找内存中的变量a并将其置于栈的顶部
  2. BUILD_LIST 新建一个长度为0的数组并放在栈顶
  3. COMPARE_OP 迭代取出两个栈的数据,查看是否相同,并将结果存放在栈顶
  4. POP_JUMP_IF_FALSE 将栈顶的数据取出得到布尔值

在三种方法中,这是最慢的,因为需要重新开辟内存空间并通过迭代的方式比较,造成后续还需要进行垃圾清理并且速度慢。

查看长度是否为0

def compare_len(a):
    if len(a) == 0 :
        return True
    return False
dis.dis(compare_len)

"""
  2           0 LOAD_GLOBAL              0 (len)
              2 LOAD_FAST                0 (a)
              4 CALL_FUNCTION            1
              6 LOAD_CONST               1 (0)
              8 COMPARE_OP               2 (==)
             10 POP_JUMP_IF_FALSE       16
"""

该方法中,判断数组长度是否为0,一共需要6个步骤:

  1. LOAD_GLOBAL 查看len函数的定义并置于栈顶
  2. LOAD_FAST 查找变量a并置于栈顶
  3. CALL_FUNCTION 取出a并将其传递到栈顶运算得到结果并放置于栈顶
  4. LOAD_CONST 将常量0置于栈顶
  5. COMPARE_OP 这一步与上面方法相同
  6. POP_JUMP_IF_FALSE 也相同

此方法使用了内置的len函数,并且没有迭代过程,相对来说速度更快一点。

内置if判断数组布尔值

def list_if(a):
    if a:
        return False
    return True
dis.dis(list_if)
"""
  2           0 LOAD_FAST                0 (a)
              2 POP_JUMP_IF_FALSE        8

"""

此方法只有两个步骤

  1. LOAD_FAST 与前面一样,内存中查找a并放置于栈顶
  2. POP_JUMP_IF_FALSE 同上,查看栈顶数据并跳转

该方法使用的两个步骤在前面的方法中均用到,因此是最快的一个方法。 那么为什么可以直接通过POP_JUMP_IF_FALSE判断呢,这是因为python其实是由c语言编写的,它会查看c语言中变量的属性如果长度大于0那么就是true否则返回false,所以使用cpython编写python代码也可以加快程序运行速度。

总结

本文通过3种方法确认一个数组或者元组是否为空,并通过dis模块查看不同运行方法中的字节码了解程序运行中的具体运行机制。