memoryview对象
- 内存视图:简化一句话就是在不copy数据的情况下,与其他对象能够共享同一个内存地址,达到操作数据的目的,在处理大量数据的时候能够极大降低内存的开销。这个类的概念灵感来自于Numpy的数组。Numpy作者回答
- memoryview对象属性及方法:仅仅涉及部分方法属性
import array
mev= memoryview(array.array('i',[1,2,3])) # memoryview(obj) obj必须是实现了缓冲协议的对象 例如bytes(字节)、bytearray、array.array
print(mev) #打印memor对象 <memory at 0x114293ac8>
print(mev.tolist()) # [1, 2, 3] obj的列表形式
print(mev.tobytes()) # b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'
# 接上:返回字节串形式,因为在array中字节类型码是i 代表signed int(有符号整数) 2个字节 打印出 每个元素字节其实是4 bytes,
#接上:16进制表示那就是1 代表是 b'\x01\x00\x00\x00
# 接上:返回的形式中存在小端 1 16进制 4字节表示其实是:0x00 0x00 0x00 0x01
print(mev.hex()) # 两位16进制 因为是4字节 表示buffer中的数据 1(01 00 00 00) 2 (02 00 00 00)3(03 00 00 00)
c= array.array('i',[1,2000,3])
print(c.itemsize) #打印出 每个元素字节可以看出其实是4 bytes
print(c.tobytes()) # b'\x01\x00\x00\x00\xd0\x07\x00\x00\x03\x00\x00\x00' 同memoryview
- memory对数据的修改:
import array
c = array.array('i',[1,2,3])
mev= memoryview(c) # memoryview(obj) obj必须是实现了缓冲协议的对象 例如bytes(字节)、bytearray、array.array
mev[1] =4 #mev支持index slicing
print(mev[1]) # 整数索引 返回对应位置元素值 1
print('第一次修改:',c) # array('i', [1, 1, 3]) 原生数组c索引位置为1的已经被修改
k = mev[0:1] #memoryview支持切片 返回对象仍然是 memoryview
print(k) # <memory at 0x1133d0a08>
import array
c = array.array('i',[1,2,3])
mev= memoryview(c) # memoryview(obj) obj必须是实现了缓冲协议的对象 例如bytes(字节)、bytearray、array.array
print(c.itemsize) # 4
print(mev.itemsize) # mev中内容每个元素大小均是4个字节
mev_cast= mev.cast('B') # cast 函数 返回一个memoryview 并将原来的视图里面的内容进行类型转换 转换成无符号 此时每个元素只有1个字节
print(mev_cast.tolist()) # 未修改 索引3之前 [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]
mev_cast[3]=4 #
print(mev_cast.itemsize) # 1 mev_cast中每个元素大小均为1bytes
print('mev_cast :',mev_cast.hex()) # 输出结果 010000040200000003000000
print('mev:',mev.hex()) # 输出结果 010000040200000003000000
print('kaishi1',c) # 输出结果 kaishi1 array('i', [67108865, 2, 3])
print(mev_cast.tolist()) # [1, 0, 0, 4, 2, 0, 0, 0, 3, 0, 0, 0] # 之前是4个字节为1个单位 现在被拆分成1个字节为单位,总共12个元素 = 3*4,上述mev_cast[3]=4 修改的其实是该列表
对于其他类型属性需要参照各自存储方式:有符号数存在符号位,并且在计算机内存中储存的是补码。
ndarry构造方法
- numpy中常见构造方法各种博客都有介绍,今天写一下ndarry这个在官方中成为low-level构造ndarry对象的方法,在探究的过程还发现了ndarry与memoryview对象其中不少的关系,写下本文放方便以后记录学习。
-
numpy.ndarry(shape,buffer,offset,dtype,strides,order):各参数解释如下:
1.shape: 由整数组成的元组,表示创建的ndarry对象的形状,其实就是数组在各个轴上或者维度上的元素的个数;2.buffer:暴露缓冲接口的对象 用数据填充这一array数组。当我们需要buffer缓冲数据由其他来源来创建,并且这一来源还不是可以被重塑(reshape)以及view的ndarry ;这一观点来自于用指定数据创建ndarry对象。
3.offset:在buffer数据中偏移量,其实也是指代了buffer中数据的开始的位置
4.dtype:数组中数据类型
5.strudes:跨度
6.order:‘C’--代表行优先,‘F’代表列优先
-
问题背景
- buffer在创建ndarray中的作用
- buffer为None的时候:输出的数组是随机 random
import numpy as np
k = np.ndarray(shape=(4,2), dtype=int, order='c')
print(k)
out:
[[-8070450532247928832 -8070450532247928832]
[ 8027794400713703428 7811887657498193774]
[ 7815259820786999141 8245937481777164148]
[ 7310584009609916025 1125904217407488]]
- buffer不为None的时候:
import numpy as np
k = np.array([1,2,3,4])
num = np.ndarray(shape=(2,1),buffer=k,dtype=int,offset=1)
# num = np.ndarray(shape=(2,1),buffer=k.data,dtype=int,offset=1) 上述中传入buffer的其实是作为ndarray对象k中的data (Python buffer object pointing to the start of the array’s data.)
print(k.data.__class__) # <class 'memoryview'> 代表一个memory对象
import numpy as np
k = np.array([1,2,3,4])
# 现在来分别测试 buffer 以及offset
#buffer
num_buffer = np.ndarray(shape=(2,1),buffer=k,dtype=int)
print(num_buffer.data.hex()) # 01000000000000000200000000000000
print(num_buffer.tolist()) # [[1], [2]]
print(num_buffer.tobytes()) # b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00' 小端模式返回字节数据
print(num_buffer.itemsize) # 每个元素对应大小是8个字节 [1]对应字节是 :b'\x01\x00\x00\x00\x00\x00\x00\x00 内存存储形式 同上
# buffer and offset
num = np.ndarray(shape=(2,1),buffer=k,dtype=int,offset=1)
print(num.data.hex()) # 00000000000000020000000000000003
print(num.tolist()) # [[144115188075855872], [216172782113783808]]
print(num.tobytes()) # b'\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03'小端模式返回字节数据
print(num.itemsize) # 每个元素对应大小是8个字节 [144115188075855872]对应字节是 :b'\x00\x00\x00\x00\x00\x00\x00\x02 内存存储形式 同上
import struct
value = struct.unpack('<q',b'\x00\x00\x00\x00\x00\x00\x00\x02') #将字节按照指定格式转换为数值,因为需要转化成8个字节大小的数据 所以数据类型选用了'q'
print(value) # value 144115188075855872
# 在num中每个数据类型是int64位,8个字节,以下分别代表offset不同偏移位置
#-2b'\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00'
#-1b'\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00'
# b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00
#0 b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00'
#1 b'\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03'
#2 b'\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00'
#3 b'\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00'
#4 b'\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00'
#5 b'\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00'
#6 b'\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00'
#7 b'\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00'
#8 b'\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'
#9 b'\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04'
#10 b'\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00'
#11 b'\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00'
12 b'\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00'
13 b'\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00'
'''
#当偏移量是itemsize =8 的整数倍的时候就相当于对对buffer数据源进行切片操作
nu1 = np.ndarray(shape=(2,1),buffer=k,dtype=int,offset=8)
print(nu1) # [[2] [3]]
value = k[1:3].reshape(2,1) #偏移8个字节 相当于1个元素,所以起始所以为1,总共包含2个元素 end-1=2 即为3
print(value) # [[2] [3]]
总结
虽然创建ndarry对象基本不会使用numpy.ndarray这个创造对象方法,但是对于背后的了解其实发现涉及到缓冲区、字节数据转换、计算机存储形式、内存视图(view)概念有很多帮助。
在查看numpy概念中了解到背后使用了C语言中指针 结构体,所以接下来将会进一步深化对概念的理解。