array.array()能用来定义二维数组吗?

1 投票
3 回答
1738 浏览
提问于 2025-04-17 21:16

我刚开始学Python,现在用的是Python 2.7。
我想创建一个二维数组,我知道怎么用列表来做。但是用列表的话,数据量大了就会占用很多内存。
为了节省内存,我想用数组而不是列表。这个想法是受到一个回答的启发,里面提到“用array.array('l')来代替列表,适合存储(整数)值”,这个回答可以在加载大字典时的巨大内存使用中找到。

请问这种方法可以用于二维数组吗?

3 个回答

-1

在Python中,数组就是列表。

在另一个问题中提到的内存优势,是因为没有使用字典。

一般来说,你不会发现把“列表换成二维数组”能节省内存。

给我看看你的数据样本,我会更新我的回答。

3

你不能真的创建一个二维的 array.array(),因为它的元素类型只能是字符、整数和浮点数。相反,你可以把数据存储在一个普通的一维数组里,然后通过一些辅助函数来访问这些数据。

下面是我想描述的一个示例:

from array import array

INFO_SIZE = 3  # Number of entries used to store info at beginning of array.
WIDTH, HEIGHT = 1000, 1000  # Dimensions.

array2d = array('l', (0 for _ in range(INFO_SIZE + WIDTH*HEIGHT)))
array2d[:INFO_SIZE] = array('l', (INFO_SIZE, WIDTH, HEIGHT))  # save array info

def get_elem(two_d_array, i, j):
    info_size, width, height = two_d_array[:INFO_SIZE]
    return two_d_array[info_size + j*width + i]

def set_elem(two_d_array, i, j, value):
    info_size, width, height = two_d_array[:INFO_SIZE]
    two_d_array[info_size + j*width + i] = value


import sys
print(format(sys.getsizeof(array2d), ",d"))  # -> 4,091,896

print(get_elem(array2d, 999, 999))           # -> 0
set_elem(array2d, 999, 999, 42)
print(get_elem(array2d, 999, 999))           # -> 42

如你所见,array2d 的大小仅比数据本身稍微大一点(在这个例子中是4,000,000字节)。你可以完全不使用函数,直接在代码中计算偏移量,这样就可以避免每次访问时调用函数带来的额外开销。另一方面,如果这不是一个大问题,你还可以进一步将所有逻辑封装在一个通用的 class Array2D 中。

更新

将实现封装在一个类中

这里是我提到的那个通用的 class Array2D 的例子。它的好处是可以以更自然的数组方式使用,只需传递两个整数给索引操作符——也就是说,像这样 my_array2d[row, col] ——而不是调用独立的函数来获取或设置元素的值。

import array
from array import array as Array
import string
import sys


# Determine dictionary of valid typecodes and default initializer values.
_typecodes = dict()
for code in string.ascii_lowercase + string.ascii_uppercase:  # Assume single ASCII chars.
    initializer = 0
    try:
        Array(code, [initializer])
    except ValueError:
        continue  # Skip
    except TypeError:
        initializer = u'\x20'  # Assume it's a Unicode character.

    _typecodes[code] = initializer


class Array2D:
    """Partial implementation of preallocated 2D array.array()."""
    def __init__(self, width, height, typecode, initializer=None):
        if typecode not in _typecodes:
            raise NotImplementedError
        self.width, self.height, self._typecode = width, height, typecode
        initializer = _typecodes[typecode]
        self.data = Array(typecode, (initializer for _ in range(width * height)))

    def __getitem__(self, key):
        i, j = key
        return self.data[j*self.width + i]

    def __setitem__(self, key, value):
        i, j = key
        self.data[j*self.width + i] = value

    def __sizeof__(self):
        # Not called by sys.getsizeof() in Python 2 (although it should be).
        return sum(map(sys.getsizeof, (self.width, self.height, self.data)))

    @property
    def typecode(self):
        return self._typecode

    @property
    def itemsize(self):
        return self.data.itemsize


array2d = Array2D(1000, 1000, 'l')  # 1 million unsigned 4 byte longs.
print(format(sys.getsizeof(array2d), ',d'))  # -> 4,091,936
print(format(array2d.itemsize, ',d'))        # -> 4
print(array2d[999, 999])                     # -> 0
array2d[999, 999] = 42
print(array2d[999, 999])                     # -> 42
1

你提到的问题是关于字典,而不是数组。不过无论如何,你可以这样做,这样就创建了一个list,里面包含了4字节整数的array,并且这些整数都是初始化为零的,这实际上就相当于一个二维的array

from array import array

width, height = 1000, 1000
array2d = [array('l', (0 for _ in xrange(width))) for _ in xrange(height)]

array2d[999][999] = 42

撰写回答