美文网首页我爱编程「Python3学习笔记」读书笔记
「Python3学习笔记」读书笔记—字符串

「Python3学习笔记」读书笔记—字符串

作者: Lovemma | 来源:发表于2018-06-21 23:08 被阅读0次

    字符串字面量以成对的单引号(`)、双引号(),或可跨行的三引号(”””)语法构成,自动合并相邻的字面量。字符串支持转义、八进制、十六进制,或 Unicode 格式字符。

    使用单引号还是双引号,并没有什么特殊限制。如果文本内的引用文字使用双引号,那么外面用单引号可避免转义,更易阅读。

    通常情况下,建议遵循多数编程语言惯例,使用双引号标示。

    Python 3 中字符串存储的是 Unicode 文本,是不可变序列类型。而 Unicode 格式大小写分别表示 16 位( \u )和 32 位(\U)整数,不能混用。

    >>> "h\x69, \u6C49\U00005B57"
    'hi, 汉字'
    

    在字面量前添加标志,表示构建指定格式的字符串。

    >>> type(u"abc")
    <class 'str'>
    >>> type(b"abc")
    <class 'bytes'>
    

    最常用的原始字符串(r, raw string),它将反斜线视作字符内容,而非转义标志。这在构建类似 Windows 路径、正则表达式匹配模式之类的文法字符串时很有用。

    >>> open(r"c:\windows\readme.txt”)
    
    >>> import re
    >>> re.findall(r"\b\d+\b", "a10 100")
    ['100']
    

    操作

    字符串支持用加法或乘法运算符拼接字符串。

    >>> s = "hello"
    
    >>> s += ", world"
    >>> "-" * 10
    '----------'
    

    编译器会尝试在编译期直接计算出字面量拼接结果,可避免运行时的内存开销。不可此类优化程度有限,并不总是有效。

    >>> def test():
    ...     a = "x" + "y" + "z"
    ...     b = "a" * 10
    ...     return a, b
    ...
    >>> import dis
    >>> dis.dis(test)
      2           0 LOAD_CONST               7 ('xyz')  # 直接给出结果,省略加法结果
                  2 STORE_FAST               0 (a)
    
      3           4 LOAD_CONST               8 ('aaaaaaaaaa')   # 省略乘法运算
                  6 STORE_FAST               1 (b)
    
      4           8 LOAD_FAST                0 (a)
                 10 LOAD_FAST                1 (b)
                 12 BUILD_TUPLE              2
                 14 RETURN_VALUE
    

    多个动态字符串拼接,应优先考虑 join 或 format 方式,而不是使用 + 号操作符直接拼接,这是因为相比于多次加法运算和多次内存分配(字符串是不可变对象),join 这类函数(方法)可预先计算出总长度,一次性分配内存,随后直接复制内存数据填充。另一方面,将固定模版内容与变量分离的 format,更易阅读和维护,可参考以前的这篇记录Python连接字符串优先使用join而不是+ | M-in’s Blog

    编写代码时除保持简单外,还应具备良好的可读性。比如:判断是否包含子字符串,in、not in 操作符就比 find 方法自然,更贴近日常阅读习惯。

    >>> "py" in "python"
    True
    >>> "py" not in "python"
    False
    
    >>> "python".find("py")
    0
    

    作为序列类型,可使用索引序号访问字符串的单个字符或某一片段(切片)。Python 3 支持负索引,也就是反向从尾部以 -1 开始(索引 0 表示正向第一个字符)。

    >>> s = "0123456789"
    >>> s[2]
    '2'
    >>> s[-1]
    '9'
    >>> s[2:6]
    '2345'
    >>> s[2:-2]
    '234567'
    

    无论以哪种方式返回与原字符串内容不同的子字符串时,都可能会重新分配内存,并复制数据。

    转换

    除了与数字、Unicode 码点的转化外,最常见的是在不用编码间进行转换。Python 3 使用 bytes、bytearray 存储字节序列,不再和 str 混用。

    格式化

    Python 3.6 新增了 f-strings 支持,这在多数脚本语言里属于标配。

    使用 f 前缀标志,Python 解释器在解析大括号内的字段或表达式时,在上下文命名空间(namespace)查找同名对象进行值替换。

    >>> x = 10
    >>> y = 20
    >>> f"{x} + {y} = {x + y}"
    '10 + 20 = 30'
    
    # 除运算符外,还可以是函数调用。
    >>> f"{type(x)}"
    "<class 'int'>"
    

    完整的 format 格式化以位置序号或字段名匹配参数进行值替换,可添加对齐、填充、精度等控制。具体请参考官方文档6.1.3. Format String Syntax

    池化

    因为无处不在的名字就是字符串实例,导致字符串可能是进程里实例数量最多的类型之一。

    鉴于相同的名字会出现在不同的命名空间里,那么有必要共享实例。内容相同,且不可变,共享内存不会导致任何问题。关键是节约内存,且可省去创建新实例的开销。

    对此,Python 的做法是实现一个字符串池( intern )。

    池负责管理实例,使用者只需饮用即可。另一潜在的好处是,从池返回的字符串,只需比较指针就可知道内容是否相同,无需额外计算。可以使用池来提升哈希表等类似结构的查找性能。

    >>> import sys
    >>> "__name__" is sys.intern("__name__")
    True
    

    除以常量方式出现的名字和字面量外,动态生成的字符串一样可加入池中。如此可保证每次都引用同一对象,不会有额外的创建和分配操作。

    >>> a = "hello, world!"
    >>> b = "hello, world!"
    >>> a is b                                          # 不同实例
    False
    >>> sys.intern(a) is sys.intern("hello, world!")        # 相同实例
    True
    

    当然,一旦失去所有外部引用,池内的字符串对象一样会被回收。

    >>> a = sys.intern("hello, world!")
    >>> id(a)
    4318361584
    >>> id(sys.intern("hello, world!"))     # 有外部引用
    4318361584
    
    >>> del a                                   # 删除外部引用后被回收
    >>> id(sys.intern("hello, world!"))     # 从 id 值不同可以看到字符串是新建后入池的
    4318389808
    

    字符串池的实现算法很简单,就是简单的字典结构。
    详细可参考 Objects/unicodeobject.c : PyUnicode_InternInPlace。
    做大数据处理时,可能须创建海量主键,使用类似机制有助于减少对象数量,节约大量内存。当然,可以选择更高效的数据结构,而不一定是系统内置的字符串池。

    从博客搬运到简书:原文链接

    相关文章

      网友评论

        本文标题:「Python3学习笔记」读书笔记—字符串

        本文链接:https://www.haomeiwen.com/subject/cgkhyftx.html