Python容器选择:为何有时应避免使用列表

Python编程中,列表(List)因其广泛的用途而广受欢迎。然而,在某些情况下,可能需要考虑避免使用列表。本文将探讨Python列表的内部机制,并提供一些替代方案,以帮助数据科学家和程序员在编写更高效的代码时做出更好的决策。

Python列表的多样性

Python列表可以包含不同类型的元素,这使得它们非常灵活。例如,可以创建包含整数的列表:

L = [1, 2, 3, 4, 5]

同样,也可以创建包含字符串的列表:

L2 = [str(c) for c in L]

这展示了Python的动态类型特性,可以创建包含不同类型元素的混合列表:

L3 = [True, 3, "6", 9.0]

这种灵活性并非没有代价。为了理解为什么应该避免使用列表,需要深入了解Python的内部工作原理。

理解Python数据类型

为了在数据驱动的编程和计算中变得高效,需要深入了解数据是如何存储和操作的。对于数据科学家来说,这一点尤其重要。Python因其易用性而受到越来越多程序员的青睐,其中之一就是动态类型。与C++或Java等静态类型语言不同,Python不需要显式声明所有变量的类型。

例如,在C++中,可能会这样写代码:

int sum = 0; for (int i = 0; i <= 100; i++) sum += i; cout << sum;

而在Python中,同样的程序可以写成:

sum = 0 for i in range(101): sum += i print(sum)

这里可以看到的主要区别是,在C++中,所有变量类型都需要显式声明,而在Python中,类型是动态推断的。这意味着可以将任何类型的数据分配给任何变量。这种灵活性意味着Python变量不仅仅是一个值,它还包含了关于其类型的额外信息。

Python整数不仅仅是整数

Python解释器是用C语言编写的,因此所有的Python对象都是C结构的伪装版本,它们不仅包含其值,还包含其他信息。例如,如果在Python中声明一个变量:

x = 10

x不仅仅是一个原始整数,而是一个指向包含多个值的复合C结构的指针。如果深入挖掘,可以发现这个C结构的样子。

struct _longobject { long ob_refcnt; PyTypeObject *ob_type; size_t ob_size; long ob_digit[1]; };

因此,它包含四部分:

  • ob_refcnt,一个引用计数,用于处理内存的分配和释放。
  • ob_type,变量的类型。
  • ob_size,数据成员的大小。
  • ob_digit,变量实际代表的值。

所有这些额外信息意味着在内存和计算能力方面有更多的开销。因此,Python int对象本质上是一个指向包含有关该变量所有信息的内存位置的指针,包括包含实际整数值的内存字节。所有这些额外信息是让能够在Python中如此自由编码的原因。不仅仅是整数,Python中的所有数据类型都带有这种开销成本,然而,这种成本在结合了许多这些对象的结构中变得显著,即列表

Python列表不仅仅是列表

现在来考虑一下当使用一个由多个元素组成的标准Python容器时会发生什么。标准Python容器是列表,类似于C中的数组,两者都是可变的,但正如之前讨论的,列表可以是异构的。

但这种灵活性是非常昂贵的。为了成为异构的,列表中的每个元素都必须包含自己的类型信息、引用计数和所有其他信息。换句话说,每个项目都是一个完整的Python对象。

因此,如果进一步分解,Python列表包含一个指针,该指针指向另一个指针块,而在该块中,所有这些指针反过来指向一个单独的完整的Python对象,就像之前看到的那样。这就像一个大型的俄罗斯套娃!

当所有变量都是同一类型时,这些信息中的大部分变得多余。难道没有更有效的容器来存储这样的数据吗?这样的容器没有这样的冗余或不太有用的信息,但仍然保留了列表的有用功能?好吧,在下一节为准备了一些替代方案。

Python有一个内置模块名为‘array’,它类似于C或C++中的数组。在这个容器中,数据存储在一块连续的内存中。就像C或C++中的数组一样,这些数组一次只支持一种数据类型,因此它不像Python列表那样异构。索引与列表相似。数组的类型必须使用官方文档中提供的typecode指定。

from array import array a = array("l", range(10)) print(a)

使用的“l”是指定想要的数组的typecode,即signed long。一些有用的方法可以与数组一起使用:

  • array.typecode – 返回数组的typecode
  • array.itemsize – 返回一个数组元素的长度(以字节为单位)。
  • array.append(x) – 在数组的右侧追加一个新元素x。
  • array.count(x) – 返回x在数组中出现的次数。
  • array.extend(iterable) – 将所有项目追加到数组的右侧。
  • NumPy数组是同质的和连续的,而由于其灵活性,列表需要更多的空间并且不是连续的。
  • NumPy中,所有任务被分解成小段,然后所有这些段并行处理。
  • 所有NumPy函数和方法都是用C、C++和Fortran等语言实现的,这些语言的执行时间比Python短得多。
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485