- 前言
- 目标读者
- 非目标读者
- 本书的结构
- 以实践为基础
- 硬件
- 杂谈:个人的一点看法
- Python 术语表
- Python 版本表
- 排版约定
- 使用代码示例
- 第一部分 序幕
- 第 1 章 Python 数据模型
- 第二部分 数据结构
- 第 2 章 序列构成的数组
- 第 3 章 字典和集合
- 第 4 章 文本和字节序列
- 第三部分 把函数视作对象
- 第 5 章 一等函数
- 第 6 章 使用一等函数实现设计模式
- 第 7 章 函数装饰器和闭包
- 第四部分 面向对象惯用法
- 第 8 章 对象引用、可变性和垃圾回收
- 第 9 章 符合 Python 风格的对象
- 第 10 章 序列的修改、散列和切片
- 第 11 章 接口:从协议到抽象基类
- 第 12 章 继承的优缺点
- 第 13 章 正确重载运算符
- 第五部分 控制流程
- 第 14 章 可迭代的对象、迭代器和生成器
- 14.1 Sentence 类第1版:单词序列
- 14.2 可迭代的对象与迭代器的对比
- 14.3 Sentence 类第2版:典型的迭代器
- 14.4 Sentence 类第3版:生成器函数
- 14.5 Sentence 类第4版:惰性实现
- 14.6 Sentence 类第5版:生成器表达式
- 14.7 何时使用生成器表达式
- 14.8 另一个示例:等差数列生成器
- 14.9 标准库中的生成器函数
- 14.10 Python 3.3 中新出现的句法:yield from
- 14.11 可迭代的归约函数
- 14.12 深入分析 iter 函数
- 14.13 案例分析:在数据库转换工具中使用生成器
- 14.14 把生成器当成协程
- 14.15 本章小结
- 14.16 延伸阅读
- 第 15 章 上下文管理器和 else 块
- 第 16 章 协程
- 第 17 章 使用期物处理并发
- 第 18 章 使用 asyncio 包处理并发
- 第六部分 元编程
- 第 19 章 动态属性和特性
- 第 20 章 属性描述符
- 第 21 章 类元编程
- 结语
- 延伸阅读
- 附录 A 辅助脚本
- Python 术语表
- 作者简介
- 关于封面
4.2 字节概要
新的二进制序列类型在很多方面与 Python 2 的 str 类型不同。首先要知道,Python 内置了两种基本的二进制序列类型:Python 3 引入的不可变 bytes 类型和 Python 2.6 添加的可变 bytearray 类型。(Python 2.6 也引入了 bytes 类型,但那只不过是 str 类型的别名,与 Python 3 的 bytes 类型不同。)
bytes 或 bytearray 对象的各个元素是介于 0~255(含)之间的整数,而不像 Python 2 的 str 对象那样是单个的字符。然而,二进制序列的切片始终是同一类型的二进制序列,包括长度为 1 的切片,如示例 4-2 所示。
示例 4-2 包含 5 个字节的 bytes 和 bytearray 对象
>>> cafe = bytes('café', encoding='utf_8') ➊ >>> cafe b'caf\xc3\xa9' >>> cafe[0] ➋ 99 >>> cafe[:1] ➌ b'c' >>> cafe_arr = bytearray(cafe) >>> cafe_arr ➍ bytearray(b'caf\xc3\xa9') >>> cafe_arr[-1:] ➎ bytearray(b'\xa9')
❶ bytes 对象可以从 str 对象使用给定的编码构建。
❷ 各个元素是 range(256) 内的整数。
❸ bytes 对象的切片还是 bytes 对象,即使是只有一个字节的切片。
❹ bytearray 对象没有字面量句法,而是以 bytearray() 和字节序列字面量参数的形式显示。
❺ bytearray 对象的切片还是 bytearray 对象。
my_bytes[0] 获取的是一个整数,而 my_bytes[:1] 返回的是一个长度为 1 的 bytes 对象——这一点应该不会让人意外。s[0] == s[:1] 只对 str 这个序列类型成立。不过,str 类型的这个行为十分罕见。对其他各个序列类型来说,s[i] 返回一个元素,而 s[i:i+1] 返回一个相同类型的序列,里面是 s[i] 元素。
虽然二进制序列其实是整数序列,但是它们的字面量表示法表明其中有 ASCII 文本。因此,各个字节的值可能会使用下列三种不同的方式显示。
可打印的 ASCII 范围内的字节(从空格到 ~),使用 ASCII 字符本身。
制表符、换行符、回车符和 \ 对应的字节,使用转义序列 \t、\n、\r 和 \\。
其他字节的值,使用十六进制转义序列(例如,\x00 是空字节)。
因此,在示例 4-2 中,我们看到的是 b'caf\xc3\xa9':前 3 个字节 b'caf' 在可打印的 ASCII 范围内,后两个字节则不然。
除了格式化方法(format 和 format_map)和几个处理 Unicode 数据的方法(包括 casefold、isdecimal、isidentifier、isnumeric、isprintable 和 encode)之外,str 类型的其他方法都支持 bytes 和 bytearray 类型。这意味着,我们可以使用熟悉的字符串方法处理二进制序列,如 endswith、replace、strip、translate、upper 等,只有少数几个其他方法的参数是 bytes 对象,而不是 str 对象。此外,如果正则表达式编译自二进制序列而不是字符串,re 模块中的正则表达式函数也能处理二进制序列。Python 3.0~3.4 不能使用 % 运算符处理二进制序列,但是根据“PEP 461—Adding % formatting to bytes and bytearray”,Python 3.5 应该会支持。
二进制序列有个类方法是 str 没有的,名为 fromhex,它的作用是解析十六进制数字对(数字对之间的空格是可选的),构建二进制序列:
>>> bytes.fromhex('31 4B CE A9') b'1K\xce\xa9'
构建 bytes 或 bytearray 实例还可以调用各自的构造方法,传入下述参数。
一个 str 对象和一个 encoding 关键字参数。
一个可迭代对象,提供 0~255 之间的数值。
一个整数,使用空字节创建对应长度的二进制序列。[Python 3.5 会把这个构造方法标记为“过时的”,Python 3.6 会将其删除。参见“PEP 467—Minor API improvements for binary sequences”。]
一个实现了缓冲协议的对象(如 bytes、bytearray、memoryview、array.array);此时,把源对象中的字节序列复制到新建的二进制序列中。
使用缓冲类对象构建二进制序列是一种低层操作,可能涉及类型转换。示例 4-3 做了演示。
示例 4-3 使用数组中的原始数据初始化 bytes 对象
>>> import array >>> numbers = array.array('h', [-2, -1, 0, 1, 2]) ➊ >>> octets = bytes(numbers) ➋ >>> octets b'\xfe\xff\xff\xff\x00\x00\x01\x00\x02\x00' ➌
➊ 指定类型代码 h,创建一个短整数(16 位)数组。
➋ octets 保存组成 numbers 的字节序列的副本。
➌ 这些是表示那 5 个短整数的 10 个字节。
使用缓冲类对象创建 bytes 或 bytearray 对象时,始终复制源对象中的字节序列。与之相反,memoryview 对象允许在二进制数据结构之间共享内存。如果想从二进制序列中提取结构化信息,struct 模块是重要的工具。下一节会使用这个模块处理 bytes 和 memoryview 对象。
结构体和内存视图
struct 模块提供了一些函数,把打包的字节序列转换成不同类型字段组成的元组,还有一些函数用于执行反向转换,把元组转换成打包的字节序列。struct 模块能处理 bytes、bytearray 和 memoryview 对象。
如 2.9.2 节所述,memoryview 类不是用于创建或存储字节序列的,而是共享内存,让你访问其他二进制序列、打包的数组和缓冲中的数据切片,而无需复制字节序列,例如 Python Imaging Library(PIL)2 就是这样处理图像的。
2Pillow 是 PIL 最活跃的派生库。
示例 4-4 展示了如何使用 memoryview 和 struct 提取一个 GIF 图像的宽度和高度。
示例 4-4 使用 memoryview 和 struct 查看一个 GIF 图像的首部
>>> import struct >>> fmt = '<3s3sHH' # ➊ >>> with open('filter.gif', 'rb') as fp: ... img = memoryview(fp.read()) # ➋ ... >>> header = img[:10] # ➌ >>> bytes(header) # ➍ b'GIF89a+\x02\xe6\x00' >>> struct.unpack(fmt, header) # ➎ (b'GIF', b'89a', 555, 230) >>> del header # ➏ >>> del img
❶ 结构体的格式:< 是小字节序,3s3s 是两个 3 字节序列,HH 是两个 16 位二进制整数。
❷ 使用内存中的文件内容创建一个 memoryview 对象……
❸ ……然后使用它的切片再创建一个 memoryview 对象;这里不会复制字节序列。
❹ 转换成字节序列,这只是为了显示;这里复制了 10 字节。
❺ 拆包 memoryview 对象,得到一个元组,包含类型、版本、宽度和高度。
❻ 删除引用,释放 memoryview 实例所占的内存。
注意,memoryview 对象的切片是一个新 memoryview 对象,而且不会复制字节序列。 [本书的技术审校之一 Leonardo Rochael 指出,如果使用 mmap 模块把图像打开为内存映射文件,那么会复制少量字节。本书不会讨论 mmap,如果你经常读取和修改二进制文件,可以阅读“mmap—Memory-mapped file support”来进一步学习。]
本书不会深入介绍 memoryview 和 struct 模块,如果要处理二进制数据,可以阅读它们的文档:“Built-in Types » Memory Views”和“struct—Interpret bytes as packed binary data”。
简要探讨 Python 的二进制序列类型之后,下面说明如何在它们和字符串之间转换。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论