说明
《Python 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
Python 标准库 array 提供了一个高效的数值数组,可以紧凑地表示基本类型值的数组:字符、整数、浮点数等。它比任何数据结构适合存放数值型的数据。数组属于序列类型,其行为与列表非常相似,不同之处在于其中存储的对象类型是受限的。 它的类型由创建时使用单个字符的 类型码 来指定。
如果我们需要一个只包含数字的列表,那么 array.array
比 list 更高效。数组支持所有跟可变序列有关的操作,包括 .pop
、.insert
和 .extend
。数组还提供从文件读取和存入文件的更快的方法,如 .frombytes
和 .tofile
。
Python 数组跟 C 语言数组一样精简。创建数组需要一个类型码,这个类型码用来表示在底层的 C 语言应该存放怎样的数据类型。比如 b 类型码代表的是有符号的字符(signedchar),array(‘b’)
创建出的数组就只能存放一个字节大小的整数,范围从 -128 到 127,这样在序列很大的时候,我们能节省很多空间。
from array import array
import random
# 构造方法如下
# array.array(typecode[, initializer])
# 构造一个空的int类型数组
arr = array('i')
# 指定内容
arr = array('i', [0, 1, 2, 3, 4, 6, 7, 8, 9, 100])
array('f',[random.randrange(-10,10) for _ in range(5)])
# array('f', [-10.0, -3.0, 8.0, -1.0, 7.0])
以下是类型码及对应的 C 类型:
类型码 | C 类型 | Python 类型 | 以字节表示的最小尺寸 | 注释 |
---|---|---|---|---|
'b' | signed char | int | 1 | |
'B' | unsigned char | int | 1 | |
'u' | wchar_t | Unicode 字符 | 2 | 注1 |
'h' | signed short | int | 2 | |
'H' | unsigned short | int | 2 | |
'i' | signed int | int | 2 | |
'I' | unsigned int | int | 2 | |
'l' | signed long | int | 4 | |
'L' | unsigned long | int | 4 | |
'q' | signed long long | int | 8 | |
'Q' | unsigned long long | int | 8 | |
'f' | float | float | 4 | |
'd' | double | float | 8 |
注1:由于平台的不同它可能为 16 位或 32 位。在 3.9 版更改: array('u') 现在使用 wchar_t 作为 C 类型而不再是已弃用的 Py_UNICODE。 这个改变不会影响其行为,因为 Py_UNICODE 自 Python 3.3 起就是 wchar_t 的别名。自版本 3.3 起不推荐使用,将在版本 4.0 (还不知道版本号是啥呢!)中删除。
值的实际表示会由机器的架构决定(严格地说是由 C 实现决定)。 实际大小可通过 itemsize 属性来获取。
array 的类型定义为 class array.array(typecode[, initializer])
,一个包含由 typecode 限制类型的条目的新数组,并由可选的 initializer 值进行初始化,该值必须为一个列表、bytes-like object 或包含正确类型元素的可迭代对象。
如果给定一个列表或字符串,该 initializer 会被传给新数组的 fromlist(), frombytes() 或 fromunicode() 方法(见下文)以将初始条目添加到数组中。 否则会将可迭代对象作为 initializer 传给 extend() 方法。
它会引发一个 审计事件 array.__new__
附带参数 typecode, initializer。
import array
# 用列表初始化
array.array('i', [1,2,3,4])
# array('i', [1, 2, 3, 4])
# 可迭代对象
array.array('i', range(5))
# array('i', [0, 1, 2, 3, 4])
# 所有可用类型码的字符串
array.typecodes
# 'bBuhHiIlLqQfd'
数组对象支持普通的序列操作如索引、切片、拼接和重复等。 当使用切片赋值时,所赋的值必须为具有相同类型码的数组对象;所有其他情况都将引发 TypeError。 数组对象也实现了缓冲区接口,可以用于所有支持 字节类对象 的场合。
以下是一些基本属性:
arr = array.array('i', range(5))
# array('i', [0, 1, 2, 3, 4])
# 类型码字符
arr.typecode
# 'i'
# 在内部表示中一个数组项的字节长度
arr.itemsize
# 4
以字节表示的内存缓冲区大小可通过以下方法的 array.buffer_info()[1] * array.itemsize 来计算。 这在使用需要内存地址的低层级(因此不够安全) I/O 接口时会很有用,例如某些 ioctl() 操作。 只要数组存在并且没有应用改变长度的操作,返回数值就是有效的。
# 元组 (address, length) 以给出用于存放
# 数组内容的缓冲区元素的当前内存地址和长度
arr.buffer_info()
# (140332510367984, 6)
“字节对调”(每个元素中的字节进行大小端转换)所有数组项。 此方法只支持大小为 1, 2, 4 或 8 字节的值;对于其他值类型将引发 RuntimeError。 它适用于从不同字节序机器所生成的文件中读取数据的情况。
# “字节对调”所有数组项
arr.byteswap()
arr
# array('i', [0, 16777216, 33554432, 50331648, 67108864, 83886080])
对调中的从小端是:机器存放数据有先存放高字节的、也有先存放低字节的;网络传输数据是一般先传输高字节。机器字节序可分为大端序(Big-endian)和小端序(little-endian)。
大端:数据的高位字节存放在低地址内,数据的低位字节存放在高地址内。
小端:数据的高位字节存放在高地址内,数据的低位字节存放在低地址内。(低低,高高)
一个整型是4个字节,如:0x1a2b3c4d。电脑读取内存数据时,是从低位地址到高位地址进行读取(从左到右)。
在小端机器中从低地址到高地址的存放方式为:0x4d,0x3c,0x2b,0x1a;(低地址存低位,高地址存高位),地址从低到高增长
在大端机器中从低地址到高地址的存放方式为:0x1a,0x2b,0x3c,0x4d;(低地址存高位)
arr = array.array('i', range(5))
# iterable 的项添加到数组末尾,类型码需要相同
arr.extend(range(2))
arr
# array('i', [0, 1, 2, 3, 4, 0, 1])
# 值在数组中的出现次数
arr.count(0) # 2
# arr.count(5) # 0
字节序列(bytes)主要是用于二进制的数据处理的数据类型,bytes 是 byte 的序列,而 str 是 unicode 的序列,是 Python 3.x 新增的类型。
字节序列(bytes)和字符串(string)的对比:
bytes 只负责以字节序列的形式(二进制形式)来存储数据,至于这些数据到底表示什么内容(字符串、数字、图片、音频等),完全由程序的解析方式决定。如果采用合适的字符编码方式(字符集),字节串可以恢复成字符串;反之亦然,字符串也可以转换成字节串。说白了,bytes 只是简单地记录内存中的原始数据,至于如何使用这些数据,bytes 并不在意,你想怎么使用就怎么使用,bytes 并不约束你的行为。
bytes 类型的数据非常适合在互联网上传输,可以用于网络通信编程;bytes 也可以用来存储图片、音频、视频等二进制格式的文件。字符串和 bytes 存在着千丝万缕的联系,我们可以通过字符串来创建 bytes 对象,或者说将字符串转换成 bytes 对象。有以下三种方法可以达到这个目的:
array.frombytes(s) 添加来自字符串的项,将字符串解读为机器值的数组(相当于使用 fromfile() 方法从文件中读取数据)。Python 3.2 将 fromstring() 重命名为 frombytes() 以使其含义更清晰。
# 添加来自字符串的项
# 将字符串解读为机器值的数组
arr.frombytes(b'love')
arr
# array('i', [0, 1, 2, 3, 4, 0, 1, 1702260588])
array.index(x) 返回最小的 i 使得 i 为 x 在数组中首次出现的序号。
arr = array.array('u', [*'abc'])
arr
# array('u', 'abc')
arr.index('b')
# 1
array.reverse() 反转数组中各项的顺序。
arr = array.array('u', [*'abc'])
arr
# array('u', 'abc')
arr.reverse()
arr
# array('u', 'cba')
array.tobytes() 将数组转换为一个机器值数组并返回其字节表示(即相当与通过 tofile() 方法写入到文件的字节序列。)Python 3.2 将 tostring() 被重命名为 tobytes() 以使其含义更清晰。
arr = array.array('u', [*'abc'])
arr
# array('u', 'abc')
arr.tobytes()
# b'a\x00\x00\x00b\x00\x00\x00c\x00\x00\x00'
array.tofile(f) 将所有项(作为机器值)写入到文件对象(file object)f。
arr = array.array('u', [*'abc'])
arr
# array('u', 'abc')
with open('text.txt', 'rb+') as f:
arr.tofile(f)
array.tolist() 将数组转换为包含相同项的普通列表。
arr = array.array('u', [*'abc'])
arr
# array('u', 'abc')
arr.tolist()
# ['a', 'b', 'c']
array.tounicode() 将数组转换为一个 Unicode 字符串。 数组必须是类型为 'u' 的数组;否则将引发 ValueError。 请使用 array.tobytes().decode(enc) 来从其他类型的数组生成 Unicode 字符串。
arr = array.array('u', b'a\x00\x00\x00b\x00\x00\x00c\x00\x00\x00')
arr
# array('u', 'abc')
arr.tounicode()
# 'abc'
当一个数组对象被打印或转换为字符串时,它会表示为 array(typecode, initializer)。 如果数组为空则 initializer 会被省略,否则如果 typecode 为 'u' 则它是一个字符串,否则它是一个数字列表。 使用 eval() 保证能将字符串转换回具有相同类型和值的数组,只要 array 类已通过 from array import array 被引入。 例如:
array('l')
array('u', 'hello \u2641')
array('l', [1, 2, 3, 4, 5])
array('d', [1.0, 2.0, 3.14])
array.append(x) 添加一个值为 x 的新项到数组末尾。
arr = array.array('i', range(5))
# array('i', [0, 1, 2, 3, 4])
# 添加一个值的新项到数组末尾
arr.append(5)
arr
# array('i', [0, 1, 2, 3, 4, 5])
array.fromfile(f, n) 从 file object f 中读取 n 项并将它们添加到数组末尾。 如果可用数据少于 n 项,则会引发 EOFError,但可用的项仍然会被插入数组。
with open('text.txt', 'rb+') as f:
arr.fromfile(f, 3)
array.fromlist(list) 添加来自 list 的项。 这等价于 for x in list: a.append(x)
,区别在于如果发生类型错误,数组将不会被改变。
arr = array.array('i', range(5))
arr
# array('i', [0, 1, 2, 3, 4])
arr.fromlist([5, 6])
arr
# array('i', [0, 1, 2, 3, 4, 5, 6])
array.fromunicode(s) 使用来自给定 Unicode 字符串的数组扩展数组。 数组必须是类型为 'u' 的数组;否则将引发 ValueError。 请使用 array.frombytes(unicodestring.encode(enc))
来将 Unicode 数据添加到其他类型的数组。
arr = array.array('u', [*'abc'])
arr
# array('u', 'abc')
arr.fromunicode('ed')
arr
# array('u', 'abced')
array.insert(i, x) 将值 x 作为新项插入数组的 i 位置之前。 负值将被视为相对于数组末尾的位置。
arr = array.array('u', [*'abc'])
arr
# array('u', 'abc')
arr.insert(0, 'x')
arr
# array('u', 'xabc')
array.pop([i]) 从数组中移除序号为 i 的项并将其返回。 可选参数值默认为 -1,因此默认将移除并返回末尾项。
arr = array.array('u', [*'abc'])
arr
# array('u', 'abc')
arr.pop(1)
# 'b'
arr
# array('u', 'ac')
array.remove(x) 从数组中移除首次出现的 x。
arr = array.array('u', [*'abc'])
arr
# array('u', 'abc')
arr.remove('b')
arr
# array('u', 'ac')
在 Python 中,我们可以将列表视为数组。但是,我们不能限制存储在列表中的元素的类型。 例如:
# elements of different types
a = [1, 3.5, "Hello"]
如果使用 array 模块创建数组,则数组的所有元素必须是相同的数值类型。
import array as arr
# Error
a = arr.array('d', [1, 3.5, "Hello"])
'''
Traceback (most recent call last):
File "<string>", line 3, in <module>
a = arr.array('d', [1, 3.5, "Hello"])
TypeError: must be real number, not str
'''
列表比数组灵活得多。它们可以存储不同数据类型的元素,包括字符串。而且,如果您需要对数组和矩阵进行数学计算,最好使用 NumPy 之类的东西。
那么,从 Python array 模块创建的数组有什么用途呢?
array.array 类型只是 C 数组的一个瘦包装器(thin wrapper),它提供了基本 C 样式数据类型的空间有效存储。 如果您需要分配一个您知道不会更改的数组,那么数组可以比列表更快并且使用更少的内存。
除非您真的不需要数组(可能需要数组模块来与 C 代码接口),否则不建议使用数组模块。
Python 虽然也提供了 array 模块,但其只支持一维数组,不支持多维数组,也没有各种运算函数。因而不适合数值运算。NumPy 的出现弥补了这些不足。
array 可以紧凑地表示一个基本值的数组:字符,整数,浮点数。数组是序列类型,表现得非常像列表,除了存储在它们中的对象的类型是受约束的。
更新时间:2022-06-29 18:04:18 标签:python array