python字节数组在哪里使用



我最近在python中发现了名为bytearray的dataType。有人能提供需要字节数组的场景吗?

这个答案被无耻地从这里剽窃了

示例1:从片段组装消息

假设您正在编写一些网络代码,该代码在套接字连接上接收到一条大消息。如果您了解套接字,就知道recv()操作不会等待所有数据到达。相反,它只是返回系统缓冲区中当前可用的内容。因此,为了获得所有数据,您可以编写如下代码:

# remaining = number of bytes being received (determined already)
msg = b""
while remaining > 0:
    chunk = s.recv(remaining)    # Get available data
    msg += chunk                 # Add it to the message
    remaining -= len(chunk)  

此代码的唯一问题是串联(+=)具有糟糕的性能。因此,Python 2中常见的性能优化是收集列表中的所有块,并在完成后执行连接。像这样:

# remaining = number of bytes being received (determined already)
msgparts = []
while remaining > 0:
    chunk = s.recv(remaining)    # Get available data
    msgparts.append(chunk)       # Add it to list of chunks
    remaining -= len(chunk)  
msg = b"".join(msgparts)          # Make the final message

现在,这里有第三个使用bytearray:的解决方案

# remaining = number of bytes being received (determined already)
msg = bytearray()
while remaining > 0:
    chunk = s.recv(remaining)    # Get available data
    msg.extend(chunk)            # Add to message
    remaining -= len(chunk)  

请注意bytearray版本是多么干净。你不会在列表中收集零件,也不会在最后执行神秘的连接。美好的

当然,最大的问题是它是否能发挥作用。为了测试这一点,我首先列出了一个小字节片段列表,如下所示:

chunks = [b"x"*16]*512

然后,我使用timeit模块来比较以下两个代码片段:

# Version 1
msgparts = []
for chunk in chunks:
    msgparts.append(chunk)
msg = b"".join(msgparts)
#Version 2
msg = bytearray()
for chunk in chunks:
    msg.extend(chunk)

测试时,代码的版本1运行时间为99.8s,而版本2运行时间为116.6s(相比之下,使用+=级联的版本需要230.3s)。因此,虽然执行联接操作仍然更快,但它只快了大约16%。就我个人而言,我认为bytearray版本更干净的编程可能会弥补这一点

示例2:二进制记录打包

这个例子是对上一个例子的轻微改动。假设您有一个包含整数(x,y)坐标的大型Python列表。类似于以下内容:points = [(1,2),(3,4),(9,10),(23,14),(50,90),...]现在,假设您需要将数据写成一个二进制编码文件,该文件由一个32位整数长度和一对32位整数组成。一种方法是使用这样的结构模块:

import struct
f = open("points.bin","wb")
f.write(struct.pack("I",len(points)))
for x,y in points:
    f.write(struct.pack("II",x,y))
f.close()

此代码的唯一问题是它执行大量的小型write()操作。另一种方法是将所有内容打包到bytearray中,最后只执行一次写入。例如:

import struct
f = open("points.bin","wb")
msg = bytearray()
msg.extend(struct.pack("I",len(points))
for x,y in points:
    msg.extend(struct.pack("II",x,y))
f.write(msg)
f.close()

果不其然,使用bytearray的版本运行得更快。在一个包含100000个点的简单计时测试中,它的运行时间大约是进行大量小写入的版本的一半。

示例3:字节值的数学处理

字节数组本身是整数数组,这使得执行某些类型的计算变得更容易。在最近的一个嵌入式系统项目中,我使用Python通过串行端口与设备通信。作为通信协议的一部分,所有消息都必须使用纵向冗余校验(LRC)字节进行签名。LRC是通过对所有字节值进行XOR来计算的。Bytearrays使这种计算变得容易。这里有一个版本:

message = bytearray(...)     # Message already created
lrc = 0
for b in message:
    lrc ^= b
message.append(lrc)          # Add to the end of the message

以下是一个提高工作安全性的版本:message.append(functools.reduce(lambda x,y:x^y,message))以下是Python 2中没有bytearray的相同计算:

message = "..."       # Message already created
lrc = 0
for b in message:
    lrc ^= ord(b)
message += chr(lrc)        # Add the LRC byte

就我个人而言,我喜欢bytearray版本。不需要使用ord(),只需将结果附加在消息的末尾,而不需要使用串联。

这是另一个可爱的例子。假设您想通过一个简单的XOR密码运行bytearray。这里有一句话:

>>> key = 37
>>> message = bytearray(b"Hello World")
>>> s = bytearray(x ^ key for x in message)
>>> s
bytearray(b'm@IIJx05rJWIA')
>>> bytearray(x ^ key for x in s)
bytearray(b"Hello World")
>>> 

这是演示文稿的链接

bytearray与常规python字符串(python2.x中的str,python3中的CCD29)非常相似,但有一个重要的区别,而字符串是不可变的,bytearrays是可变的,有点像单字符串的list

这很有用,因为有些应用程序使用字节序列的方式对不可变字符串的性能很差。当您在大块内存的中间进行大量的小更改时,例如在数据库引擎或图像库中,字符串的性能非常差;因为您必须复制整个(可能很大的)字符串。CCD_ 22的优点是可以在不首先复制存储器的情况下进行这种更改。

但这种特殊情况实际上更多的是例外,而不是规则。大多数用途涉及比较字符串或字符串格式。对于后者,通常都有一个副本,所以可变类型没有任何优势,而对于前者,由于不可变的字符串不能更改,您可以计算字符串的hash并进行比较,以此作为按顺序比较每个字节的快捷方式,这几乎总是一个巨大的胜利;因此不可变类型(strbytes)是默认类型;当您需要CCD_ 26的特殊功能时,CCD_。

如果您查看bytearray的文档,它会显示:

返回一个新的字节数组。字节数组类型是0<=范围内的可变整数序列x<256.

相比之下,bytes的文档显示:

返回一个新的"字节"对象,它是一个不可变的整数序列,范围为0<=x<256.bytes是字节数组的一个不可变版本——它具有相同的非可变方法以及相同的索引和切片行为。

正如您所看到的,主要区别在于可变性。"更改"字符串的str方法实际上返回了一个具有所需修改的新字符串。而改变序列bytearray方法实际上改变了序列

如果您正在通过二进制表示编辑大型对象(例如图像的像素缓冲区),并且您希望在适当的位置进行修改以提高效率,则您更喜欢使用bytearray

维基百科提供了一个使用Python字节数组(减少了文档字符串)的XOR密码示例:

#!/usr/bin/python2.7
from os import urandom
def vernam_genkey(length):
    """Generating a key"""
    return bytearray(urandom(length))
def vernam_encrypt(plaintext, key):
    """Encrypting the message."""
    return bytearray([ord(plaintext[i]) ^ key[i] for i in xrange(len(plaintext))])
def vernam_decrypt(ciphertext, key):
    """Decrypting the message"""
    return bytearray([ciphertext[i] ^ key[i] for i in xrange(len(ciphertext))])
def main():
    myMessage = """This is a topsecret message..."""
    print 'message:',myMessage
    key = vernam_genkey(len(myMessage))
    print 'key:', str(key)
    cipherText = vernam_encrypt(myMessage, key)
    print 'cipherText:', str(cipherText)
    print 'decrypted:', vernam_decrypt(cipherText,key)
    if vernam_decrypt(vernam_encrypt(myMessage, key),key)==myMessage:
        print ('Unit Test Passed')
    else:
        print('Unit Test Failed - Check Your Python Distribution')
if __name__ == '__main__':
    main()

最新更新