【问题标题】:Reusing Python Bytearray/Memoryview重用 Python Bytearray/Memoryview
【发布时间】:2014-05-14 16:49:17
【问题描述】:

我正在尝试通过套接字接收一系列 protobuf;我不会提前知道数据量。我正在发送相当数量的邮件,并且在收到邮件时需要buffer the messages(以确保我收到所有邮件)。我想利用 Python 中可用的 bytearray/memoryview 来消除不必要的副本。

我目前正在使用字符串并在收到数据时附加数据。这很容易,我可以通过执行以下操作“转移”“缓冲区”:

# Create the buffer
str_buffer = []

# Get some data and add it to our "buffer"
str_buffer += "Hello World"

# Do something with the data . . .

# "shift"/offset the message by the data we processed
str_buffer = str_buffer[6:]

是否可以使用 bytearray/memoryview 做类似的事情?

# Create the buffer/memoryarray 
buffer = bytearray(1024)
view   = memoryview(buffer)

# I can set a single byte
view[0] = 'a'

# I can "offset" the view by the data we processed, but doing this 
# shrinks the view by 3 bytes. Doing this multiple times eventually shrinks
# the view to 0.
view = view[3:]

当我尝试在末尾添加更多数据时出现问题。如果我曾经“偏移”现有视图,视图的大小会“缩小*”,我可以添加越来越少的数据。有没有办法重用现有的内存视图并将数据向左移动?

*根据文档,我知道我无法调整数组的大小。我认为缩小的错觉是我的误解。

【问题讨论】:

  • 我已经找到那个帖子了。答案的前提是预先知道数据量。因此,您可以在开始接收数据之前分配适当大小的字节数组。它没有提到重用(小)固定大小的字节数组/内存视图。
  • 您无需提前知道发送者将发送多少数据以使用recv_from()。它总是返回它得到的字节数;如果这是您的memoryview 的大小,那么还有更多数据需要,请再次致电recv_from()
  • 在您提到的示例中,toread 是在 while 循环之外定义的。 recv_into 返回读取的数据量,是的。数据量从toread 中减去,直到为0。这似乎表明toread 的数据量是事先已知的,用于创建适当大小的字节数组。我错过了什么吗?

标签: python python-2.7 python-3.x


【解决方案1】:

你真的,老实说,你不需要提前知道有多少数据,只要继续阅读,直到你没有更多的数据:

import socket, sys

HOST = 'localhost'        # The remote host
PORT = 50007              # The same port as used by the server

recvbuff = bytearray(16)
recvview = memoryview(recvbuff)

size = 0

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True:
    nbytes = s.recv_into(recvview)
    if not nbytes:
        break
    size += nbytes
    recvview = recvview[nbytes:]
    if not len(recvview):
        print "filled a chunk", recvbuff
        recvview = memoryview(recvbuff)

print 'end of data', recvbuff[:len(recvview)], size

s.close()

【讨论】:

  • 太棒了!这帮了很多忙,所以谢谢你。我想补充一点,我能够将 memoryview 中的数据“移动”b = bytearray(view[2:]).extend('\0' * 2)(左移 2)然后v = memoryview(b)。它似乎有效,但我还不知道这是否是合适的方法。
  • 呃...我以为你想避免复制。 bytearray.extend 会将其内容复制到新分配的数组中...
  • 啊,我愿意...我正在处理收到的数据。我想我的问题是我是否有缓冲区:[a][b][c][ ][ ] 并且我确实在处理a 我如何移动缓冲区[b][c][ ][ ][ ] 并仍然添加到末尾:[b][c][d][ ][ ]。当我测试你提供的代码时,它似乎填充了缓冲区然后清除它?
  • 嗯...好吧,要么你想复制数据,要么不;你当然可以很容易地复制recvbuff[:len(recvview)] = recvview,但我不会那样做(如果你想制作这样的副本,只需使用普通的'recv)。为什么你认为你需要它?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-02
  • 1970-01-01
相关资源
最近更新 更多