从memory对象看Numpy中的ndarray对象

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语言中指针 结构体,所以接下来将会进一步深化对概念的理解。


__EOF__

本文作者ivan09
本文链接https://www.cnblogs.com/ivan09/p/14234821.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   烦恼1234  阅读(426)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示