fra*_*f95 10 python arrays numpy amazon-s3 boto3
我想使用 boto3 包将 numpy 数组上传到 S3,该包需要一个 bytes 对象。我想将此 numpy 数组转换为字节,但由于内存限制而不进行任何复制。我尝试过的方法不起作用,因为它们会创建副本:
numpy.ndarray.tobytes,numpy.ndarray.tostringbytes(arr)io.BytesIO(arr)io.BytesIO(memoryview(arr))numpy 似乎曾经提供过numpy.ndarray.getbuffer,但在以后的版本中已弃用。
有没有办法在不复制的情况下创建字节视图?
您可以利用该ctypes模块创建指向数据数组的指针,并将其转换为字节形式。
import ctypes
import numpy as np
# generate the test array
size = 0x10
dtype = np.short
bsize = 2 # size of a single np.short in bytes, set for the data type you want to upload
arr = np.arange(size, dtype=dtype)
# create a pointer to the block of memory that the array lives in, cast to char type. Note that (size*bsize) _must_ be in parenthesis for the code to run correctly.
memory_block = (ctypes.c_char*(size*bsize)).from_address(arr.ctypes.data)
print(memory_block.raw)
# b'\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\t\x00\n\x00\x0b\x00\x0c\x00\r\x00\x0e\x00\x0f\x00'
# mutate the array and check the contents at the pointer
arr[0] = 255.
print(memory_block.raw)
# b'\xff\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\t\x00\n\x00\x0b\x00\x0c\x00\r\x00\x0e\x00\x0f\x00'
Run Code Online (Sandbox Code Playgroud)
这至少似乎满足了您在问题评论中提出的测试。(即,如果我改变数组,我对它的看法会改变吗?)。
不过,这里有几点需要注意。第一,Python 字节对象是不可变的,这意味着如果将一个对象分配给变量,则会创建一个副本。
y = memory_block.raw
print(y[:2])
# b'\xff\x00'
arr[0] = 127
print(y[:2])
# b'\xff\x00'
Run Code Online (Sandbox Code Playgroud)
第二,boto3似乎想要一个类似 File 的对象,至少根据版本 1.28.1 的源代码。调用bio = BytesIO(memory_block.raw)会产生副本,这意味着我们回到了上传的起点。
下面的类ArrayUploader实现了一些基本的 IO 方法(read、seek、tell)。当read调用时,数据可能仍然从底层内存 blob 复制,这意味着空间仍然是限制因素。但是,如果设置了读取的大小,则一次仅从内存 blob 复制那么多数据。我无法告诉你如何boto3处理从对象读取的大小。IO
import ctypes
import re
from io import IOBase
import numpy as np
class ArrayUploader(IOBase):
# set this up as a child of IOBase because boto3 wants an object
# with a read method.
def __init__(self, array):
# get the number of bytes from the name of the data type
# this is a kludge; make sure it works for your case
dbits = re.search('\d+', str(np.dtype(array.dtype))).group(0)
dbytes = int(dbits) // 8
self.nbytes = array.size * dbytes
self.bufferview = (ctypes.c_char*(self.nbytes)).from_address(array.ctypes.data)
self._pos = 0
def tell(self):
return self._pos
def seek(self, pos):
self._pos = pos
def read(self, size=-1):
if size == -1:
return self.bufferview.raw[self._pos:]
old = self._pos
self._pos += size
return self.bufferview.raw[old:self._pos]
# generate the test array
size = 0x10
dtype = np.short
arr = np.arange(size, dtype=dtype)
# initialize our uploader object
arrayuploader = ArrayUploader(arr)
# read some data out
print(x:=arrayuploader.read(8))
# b'\x00\x00\x01\x00\x02\x00\x03\x00'
# mutate the array, reread the same data
arr[0] = 127
arrayuploader.seek(0)
print(y:=arrayuploader.read(8))
# b'\x7f\x00\x01\x00\x02\x00\x03\x00'
# has x changed with the original array?
print(x == y)
# False
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2337 次 |
| 最近记录: |