bytearray是可變(mutable)的字節序列,相對于Python2中的str,但str是不可變(immutable)的。
在Python3中由于str默認是unicode編碼,所以只有通過bytearray才能按字節訪問。
memoryview為支持buffer protocol[1,2]的對象提供了按字節的內存訪問接口,好處是不會有內存拷貝。
默認str和bytearray支持buffer procotol。
下面兩種行為的對比:
簡單點就是,str和bytearray的切片操作會產生新的切片str和bytearry并拷貝數據,使用memoryview之后不會。
不使用memoryview
>> a = 'aaaaaa'
>> b = a[:2] # 會產生新的字符串
>> a = bytearray('aaaaaa')
>> b = a[:2] # 會產生新的bytearray
>> b[:2] = 'bb' # 對b的改動不影響a
>> a
bytearray(b'aaaaaa')
>> b
bytearray(b'bb')
使用memoryview
>> a = 'aaaaaa'
>> ma = memoryview(a)
>> ma.readonly # 只讀的memoryview
True
>> mb = ma[:2] # 不會產生新的字符串
>> a = bytearray('aaaaaa')
>> ma = memoryview(a)
>> ma.readonly # 可寫的memoryview
False
>> mb = ma[:2] # 不會會產生新的bytearray
>> mb[:2] = 'bb' # 對mb的改動就是對ma的改動
>> mb.tobytes()
'bb'
>> ma.tobytes()
'bbaaaa'
我的使用場景是網絡程序中socket接收和接收數據的解析:
使用memoryview之前的sock接收代碼簡化如下
def read(size):
ret = ''
remain = size
while True:
data = sock.recv(remain)
ret += data # 這里不斷會有新的str對象產生
if len(data) == remain:
break
remain -= len(data)
return ret
使用meoryview之后,避免了不斷的字符串拼接和新對象的產生
def read(size):
ret = memoryview(bytearray(size))
remain = size
while True:
data = sock.recv(remain)
length = len(data)
ret[size - remain: size - remain + length] = data
if len(data) == remain:
break
remain -= len(data)
return ret
返回memoryview還有一個優點,在使用struct進行unpack解析時可以直接接收memoryview對象,非常高效(避免大的str進行分段解析時大量的切片操作)。
例如:
mv = memoryview('\x00\x01\x02\x00\x00\xff...')
type, len = struct.unpack('!BI', mv[:5])
...