对于只用 Python 2 编程的程序员来说字符串的话题可能会造成一些困惑。Python 3 中只有一种能够保存文本信息的数据类型就是 strstring字符串。它是不可变的序列保存的是 Unicode 码位code point。这是与 Python 2 的主要区别Python 2 用 str 表示字节字符串这种类型现在在 Python 3 中用 bytes 对象来处理但处理方式并不完全相同。Python 中的字符串是序列。基于这一事实应该把字符串放在其他容器类型的一节去介绍但字符串与其他容器类型在细节上有一个很重要的差异。字符串可以保存的数据类型有非常明确的限制就是 Unicode 文本。bytes 以及可变的 bytearray 与 str 不同只能用字节作为序列值即 0 x 256 范围内的整数。一开始可能会有点糊涂因为其打印结果与字符串非常相似print(bytes([102, 111, 111]))b’foo’对于 bytes 和 bytearray在转换为另一种序列类型例如 list 或 tuple时可以显示出其本来面目list(b’foo bar’)[102, 111, 111, 32, 98, 97, 114]tuple(b’foo bar’)(102, 111, 111, 32, 98, 97, 114)许多关于 Python 3 的争议都是关于打破字符串的向后兼容和 Unicode 的处理方式。从Python 3.0 开始所有没有前缀的字符串都是 Unicode。因此所有用单引号‘、双引号“或成组的 3 个引号单引号或双引号包围且没有前缀的值都表示 str 数据类型type(“some string”)class ‘str’在 Python 2 中Unicode 需要有 u 前缀例如 usome string”。从 Python 3.3 开始为保证向后兼容仍然可以使用这个前缀但它在 Python 3 中没有任何语法上的意义。前面的一些例子中已经提到过字节但为了保持前后一致我们来明确介绍它的语法。字节也被单引号、双引号或三引号包围但必须有一个 b 或 B 前缀type(bsome bytes)class ‘bytes’注意Python 语法中没有 bytearray 字面值。最后同样重要的是Unicode 字符串中包含无法用字节表示的“抽象”文本。因此如果 Unicode 字符串没有被编码为二进制数据的话是无法保存在磁盘中或通过网络发送的。将字符串对象编码为字节序列的方法有两种• 利用str.encode(encoding, errors)方法用注册编解码器registered codec对字符串进行编码。编解码器由 encoding 参数指定默认值为’utf-8’。第二个 errors 参数指定错误的处理方案可以取’strict’默认值、‘ignore’、‘replace’、‘xmlcharrefreplace’或其他任何注册的处理程序参见内置codecs 模块的文档。• 利用 bytes(source, encoding, errors)构造函数创建一个新的字节序列。如果 source 是 str 类型那么必须指定 encoding 参数它没有默认值。encoding 和 errors 参数的用法与 str.encode()方法中的相同。用类似方法可以将 bytes 表示的二进制数据转换成字符串• 利用 bytes.decode(encoding, errors)方法用注册编解码器对字节进行解码。这一方法的参数含义及其默认值与 str.encode()相同。• 利用 str(source, encoding, error)构造函数创建一个新的字符串实例。与 bytes()构造函数类似如果 source 是字节序列的话必须指定 str 函数的encoding 参数它没有默认值。实现细节Python 字符串是不可变的。字节序列也是如此。这一事实很重要因为它既有优点又有缺点。它还会影响 Python 高效处理字符串的方式。由于不变性字符串可以作为字典的键或set 的元素因为一旦初始化之后字符串的值就不会改变。另一方面每当需要修改过的字符串时即使只是微小的修改都需要创建一个全新的字符串实例。幸运的是bytearray是 bytes 的可变版本不存在这样的问题。字节数组可以通过元素赋值来进行原处修改无需创建新对象其大小也可以像列表一样动态地变化利用 append、pop、inseer 等方法。字符串拼接由于 Python 字符串是不可变的在需要合并多个字符串实例时可能会产生一些问题。如前所述拼接任意不可变序列都会生成一个新的序列对象。思考下面这个例子利用多个字符串的重复拼接操作来创建一个新字符串s “”for substring in substrings:s substring这会导致运行时间成本与字符串总长度成二次函数关系。换句话说这种方法效率极低。处理这种问题可以用 str.join()方法。它接受可迭代的字符串作为参数返回合并后的字符串。由于这是一个方法实际的做法是利用空字符串来调用它s “”.join(substrings)字符串的这一方法还可以用于在需要合并的多个子字符串之间插入分隔符看下面这个例子‘,’.join([‘some’, ‘comma’, ‘separated’, ‘values’])‘some,comma,separated,values’需要记住仅仅因为 join()方法速度更快对于大型列表来说更是如此并不意味着在所有需要拼接两个字符串的情况下都应该使用这一方法。虽然这是一种广为认可的做法但并不会提高代码的可读性。可读性是很重要的在某些情况下join()的性能可能还不如利用加法的普通拼接下面举几个例子。• 如果子字符串的数量很少而且已经包含在某个可迭代对象中那么在某些情况下创建一个新序列来进行拼接操作的开销可能会超过使用 join()节省下来的开销。• 在拼接短的字面值时由于 CPython 中的常数折叠constant folding一些复杂的字面值不只是字符串在编译时会被转换为更短的形式例如’a’ ‘b’ ‘c’被转换为’abc’。当然这只适用于相对短的常量字面值。最后如果事先知道字符串的数目可以用正确的字符串格式化方法来保证字符串拼接的最佳可读性。字符串格式化可以用 str.format()方法或%运算符。如果代码段的性能不是很重要或者优化字符串拼接节省的开销很小那么推荐使用字符串格式化作为最佳方法。
Python 高手编程系列十一:字符串与字节
发布时间:2026/6/10 1:03:59
对于只用 Python 2 编程的程序员来说字符串的话题可能会造成一些困惑。Python 3 中只有一种能够保存文本信息的数据类型就是 strstring字符串。它是不可变的序列保存的是 Unicode 码位code point。这是与 Python 2 的主要区别Python 2 用 str 表示字节字符串这种类型现在在 Python 3 中用 bytes 对象来处理但处理方式并不完全相同。Python 中的字符串是序列。基于这一事实应该把字符串放在其他容器类型的一节去介绍但字符串与其他容器类型在细节上有一个很重要的差异。字符串可以保存的数据类型有非常明确的限制就是 Unicode 文本。bytes 以及可变的 bytearray 与 str 不同只能用字节作为序列值即 0 x 256 范围内的整数。一开始可能会有点糊涂因为其打印结果与字符串非常相似print(bytes([102, 111, 111]))b’foo’对于 bytes 和 bytearray在转换为另一种序列类型例如 list 或 tuple时可以显示出其本来面目list(b’foo bar’)[102, 111, 111, 32, 98, 97, 114]tuple(b’foo bar’)(102, 111, 111, 32, 98, 97, 114)许多关于 Python 3 的争议都是关于打破字符串的向后兼容和 Unicode 的处理方式。从Python 3.0 开始所有没有前缀的字符串都是 Unicode。因此所有用单引号‘、双引号“或成组的 3 个引号单引号或双引号包围且没有前缀的值都表示 str 数据类型type(“some string”)class ‘str’在 Python 2 中Unicode 需要有 u 前缀例如 usome string”。从 Python 3.3 开始为保证向后兼容仍然可以使用这个前缀但它在 Python 3 中没有任何语法上的意义。前面的一些例子中已经提到过字节但为了保持前后一致我们来明确介绍它的语法。字节也被单引号、双引号或三引号包围但必须有一个 b 或 B 前缀type(bsome bytes)class ‘bytes’注意Python 语法中没有 bytearray 字面值。最后同样重要的是Unicode 字符串中包含无法用字节表示的“抽象”文本。因此如果 Unicode 字符串没有被编码为二进制数据的话是无法保存在磁盘中或通过网络发送的。将字符串对象编码为字节序列的方法有两种• 利用str.encode(encoding, errors)方法用注册编解码器registered codec对字符串进行编码。编解码器由 encoding 参数指定默认值为’utf-8’。第二个 errors 参数指定错误的处理方案可以取’strict’默认值、‘ignore’、‘replace’、‘xmlcharrefreplace’或其他任何注册的处理程序参见内置codecs 模块的文档。• 利用 bytes(source, encoding, errors)构造函数创建一个新的字节序列。如果 source 是 str 类型那么必须指定 encoding 参数它没有默认值。encoding 和 errors 参数的用法与 str.encode()方法中的相同。用类似方法可以将 bytes 表示的二进制数据转换成字符串• 利用 bytes.decode(encoding, errors)方法用注册编解码器对字节进行解码。这一方法的参数含义及其默认值与 str.encode()相同。• 利用 str(source, encoding, error)构造函数创建一个新的字符串实例。与 bytes()构造函数类似如果 source 是字节序列的话必须指定 str 函数的encoding 参数它没有默认值。实现细节Python 字符串是不可变的。字节序列也是如此。这一事实很重要因为它既有优点又有缺点。它还会影响 Python 高效处理字符串的方式。由于不变性字符串可以作为字典的键或set 的元素因为一旦初始化之后字符串的值就不会改变。另一方面每当需要修改过的字符串时即使只是微小的修改都需要创建一个全新的字符串实例。幸运的是bytearray是 bytes 的可变版本不存在这样的问题。字节数组可以通过元素赋值来进行原处修改无需创建新对象其大小也可以像列表一样动态地变化利用 append、pop、inseer 等方法。字符串拼接由于 Python 字符串是不可变的在需要合并多个字符串实例时可能会产生一些问题。如前所述拼接任意不可变序列都会生成一个新的序列对象。思考下面这个例子利用多个字符串的重复拼接操作来创建一个新字符串s “”for substring in substrings:s substring这会导致运行时间成本与字符串总长度成二次函数关系。换句话说这种方法效率极低。处理这种问题可以用 str.join()方法。它接受可迭代的字符串作为参数返回合并后的字符串。由于这是一个方法实际的做法是利用空字符串来调用它s “”.join(substrings)字符串的这一方法还可以用于在需要合并的多个子字符串之间插入分隔符看下面这个例子‘,’.join([‘some’, ‘comma’, ‘separated’, ‘values’])‘some,comma,separated,values’需要记住仅仅因为 join()方法速度更快对于大型列表来说更是如此并不意味着在所有需要拼接两个字符串的情况下都应该使用这一方法。虽然这是一种广为认可的做法但并不会提高代码的可读性。可读性是很重要的在某些情况下join()的性能可能还不如利用加法的普通拼接下面举几个例子。• 如果子字符串的数量很少而且已经包含在某个可迭代对象中那么在某些情况下创建一个新序列来进行拼接操作的开销可能会超过使用 join()节省下来的开销。• 在拼接短的字面值时由于 CPython 中的常数折叠constant folding一些复杂的字面值不只是字符串在编译时会被转换为更短的形式例如’a’ ‘b’ ‘c’被转换为’abc’。当然这只适用于相对短的常量字面值。最后如果事先知道字符串的数目可以用正确的字符串格式化方法来保证字符串拼接的最佳可读性。字符串格式化可以用 str.format()方法或%运算符。如果代码段的性能不是很重要或者优化字符串拼接节省的开销很小那么推荐使用字符串格式化作为最佳方法。