《Python源码剖析》之字符串拼接的一个效率问题

news2025/6/17 12:22:07

前言

我们常用的字符串拼接方法有两个,一个是通过“+”号实现字符串的拼接,还一个就是通过join方法来实现拼接,前者在写法上更加便利,和数字之间的加法运算一样,通常只有两个运算对象,只不过他们的运算规则有所不同,字符的加法规则是“拼接”,数字的加法规则是“数值相加”;而join方法处理的对象通常是多个字符串,他们使用相同的拼接符号进行拼接最终得到一个字符串。值得注意的是,除了操作对象的个数不同以外,这两个功能几乎可以相互平替对方。

例子

来看一个具体例子:分别使用"+“方法和"join"方法实现n个字符串的拼接,如果使用”+"号实现可能会相对复杂:需要一个额外的for循环,因为它一次性只能操作两个字符串,而使用join则方便很多,具体代码实现如下,当然除了简单实现这两个方法外,还实现了clock这个装饰器用来统计执行的耗时和空间大小(粗略计算)。

from tools import clock

@clock(True, False)
def add(s_list):
    res = ''
    for s in s_list:
        res += s
    return res


@clock(True, False)
def join(s_list):
    return ''.join(s_list)


def main():
    for i in range(9):
        s_count = int(pow(10, i))
        s_list = ['abc' for _ in range(s_count)]
        add(s_list)
        join(s_list)
        add_cost_time = add.cost_time
        join_cost_time = join.cost_time
        print(f'字符串的个数:{s_count} add耗时:{add_cost_time}ns join耗时:{join_cost_time}ns', end=' ')
        if join_cost_time > 0:
            print(f'add耗时是join的{add_cost_time // join_cost_time}倍')
        else:
            print()


if __name__ == '__main__':
    main()

执行结果如下:image.4d37ae48da1e11ee9de617490ed73bd0.png
通过运行结果可以发现,随着操作的字符串的个数的增加,add方法和join方法他们使用的空间大小几乎保持一致(因为得到的结果是一样的),但是从10^5这个量级开始,add方法的耗时就比join方法的耗时明显高很多,并且每增加一个量级,耗时也会相应增加一个量级。那么为什么会有这样的一个结果呢?

源码探索

如果想知道为什么,那就必须要搞清楚这两个方法的实现方式和细节,才能搞明白为什么会有如此大的差距。如果你经常看某个方法的具体实现方式的话,以join方法为例,我相信你肯定会立马按住ctrl键,然后鼠标左键(当然这里不同的编辑器和快捷键会有所差别),跳到它的源代码:image.8b96f5bcda1a11ee9de617490ed73bd0.png
可惜这次不幸的是:它只给你留下了一段注释和和一个占位符pass,通过注释可以知道,它告诉了我们join方法的功能就是通过指定的分隔符来拼接多个字符串的,但是却没有透露给你它的实现细节。
对于有一定经验的小伙伴来说可能已经猜到答案了:它的实现在"它的源码中",在更深的一个层次。没错,它的实现在python的源代码中,在c语言这一层。通过下面这个图你可能就知道了:

image.6d1b1712da1a11ee9de617490ed73bd0.png
str作为python中最常用的内建对象之一,当然也在"Objects"这个目录中。(至于它是如何找到并调用objects中对应方法的,这个问题可以留给大家去探索,虽然我也还没搞明白🙃🙃🙃 )

如何找源码

在进行源码分析之前,首要的任务就是如何找到它,最重要的一个参考就是如上的截图,它列举出来了python源代码中每个目录代表的含义,其中,Lib和Modules中包含了所有的标准库,Objects包含了所有的Python内建对象,通过这三个目录我们应该就可以找到大部分我们需要的内容了。

注意:虽然这个是py2(具体一点是py2.4左右)的,但是根据我的对比,这些目录的含义基本上是没有变化的,只不过它内部的具体内容(特别是源代码)可能发生了很大的变化,如果你的版本越高的话。就拿我现在看的是py3.11的代码来看,它的源代码几乎是重新写了一遍(虽然只对比了几处)。

join源码分析

str是python的内置对象,因此它的源代码应该在Objects目录中,具体的位置可以根据该目录下文件的命名来判断(这个方法可能有点愚蠢,因为完全凭经验,没有具体的逻辑,主要是我也没有找到更好的方法😅 )。这里join方法的实现就在这个join.h文件中,具体方法是(bytes_join)(PyObject *sep, PyObject *iterable)。我不知道大家第一眼看到这个源码的感觉是什么样的,如果你对c语言掌握的比较好的话,可能会感到很亲切;如果你像我一样只是了解一点(对c语言的掌握已经停留在了大一学习那会儿…)的话可能会比较头疼哈哈哈。不过这些都不重要,因为当我真正沉下心去看还是能够理解它的大概意思的,看的过程中特别要注意它的注释变量名(对于python这样的知名项目的源码,你绝对可以相信它取的变量名能够达到“见名之意”的作用),这两个我认为是理解的它的关键切入点。此外,还可以借助强大的AI来协助我们理解带代码,如下图所示,它基本上完全地解释了整个方法的步骤。

image.bfd50166da1f11ee9de617490ed73bd0.png

接下来我们进入正题,排除掉开头的一些逻辑判断,我们可以将这个方法的核心逻辑分为三点:

  1. 计算出序列中所有字符串拼接起来需要开辟的空间

    • 在这个循环计算的过程中可能有两个报错情况,一个是itemlen > PYSSIZE_T_MAX - szseplen > PY_SSIZE_T_MAX - sz,这个主要是防止需要开辟的空间大于最大值PYSSIZE_T_MAX
    • 另外一个报错就是sequence changed size during iteration,这个报错我相信大家比较熟悉,一不小心在循环中修改列表的大小就会报出这个错误。
  2. 申请空间

  3. 写入数据

注意:这里是先计算出需要开辟的空间,然后只进行一次空间的申请。

image.e9263facdae311ee9de617490ed73bd0.png

"+"法的源码分析

还是和上面的一样,如果对c语言了解比较浅的同学,可以借助强大的ai来协助我们一起来理解源码:

image.09987e84daea11ee9de617490ed73bd0.png
抛开一些判断的逻辑,其大致的核心逻辑如下:

  • 1.计算出旧字符串的长度
  • 2.根据旧字符串的长度之和创建新的字符串对象
  • 3.分别将旧的left和right字符串写入到新的字符串中

注意:这里创建新的对象时就会进行空间的申请

image.ffe466cade2011ee9de617490ed73bd0.png
大家有没有发现,不管是join方法还是“+”法方法,都会创建新的对象,进行一次空间申请,但是细心的小伙伴一定发现了,如果随着操作对象的数量增加,join方法始终都只需要进行一次空间的申请,而“+”法方法随着操作对象的数量的增加,它申请空间的次数也会随之增加,准确的说:如果有n(n>=2)个操作对象,那么“+”法需要进行n-1次空间申请,假设它们每次申请空间的耗时都相同,那么对n个对象进行拼接的耗时比就是:“+”法/join =n-1/1,所以上面例子是不是就说得通啦。😉

更多内容可以前往博主的个人博客系统:白日梦想园。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1503790.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

300分钟吃透分布式缓存-26讲:如何大幅成倍提升Redis处理性能?

主线程 Redis 自问世以来,广受好评,应用广泛。但相比, Memcached 单实例压测 TPS 可以高达百万,线上可以稳定跑 20~40 万而言,Redis 的单实例压测 TPS 不过 10~12 万,线上一般最高也就 2~4 万,…

7. 交叉开发环境设置

嵌入式交叉编译工具 ​ 交叉编译工具是为了使在上位机中编译的文件能够在不同平台的目标机中执行,搭建交叉编译环境是嵌入式开发的第一步,也是关键的一步。不同的体系结构、不同的操作系统,甚至是不同版本的内核,都会用到不同的交…

差距拉开了!量化大厂最新业绩排行曝光!

经历了一月份的失落和二月份绝地反攻,量化大厂们的整体业绩备受关注。 而今年2月份的量化战绩,甚为关键! 毕竟市场指数“前低后高”,基金经理与投资人开年以来,共同经历了“惊心动魄”的考验。 量化大厂&#xff0c…

帮管客CRM jiliyu接口存在SQL漏洞 附POC软件

免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。 1. 帮管客CRM简介 微信公众号搜索:南风漏洞复现文库…

分类算法入门:以鸢尾花数据集为例

近两年人工智能技术蓬勃发展,OpenAI连续放出ChatGPT、Sora等“王炸”产品,大模型、AIGC等技术带来了革命性的提升,很多人认为人工智能将引领第四次工业革命。国内各大互联网公司也是重点投资布局,从个人角度来说要尽快跟上时代的潮…

12. 建立用户表并使用雪花算法生成用户ID

文章目录 一、建立用户表二、雪花算法生成唯一ID三、将雪花算法整合到我们的项目中 一、建立用户表 上一节我们搭建完了脚手架,从这一节开始,就正式进入到业务逻辑的开发了。首先要开发的就是博客系统的用户注册与登录功能。 既然涉及到用户&#xff0…

深入浅出计算机网络 day.1 概论④ 计算机网络的定义和分类

不要退却,要绽放魅力 我的心会共鸣 和你 —— 24.3.9 一、计算机网络的定义 计算机网络早期的一个最简单定义 现阶段计算机网络的一个较好的定义 二、计算机网络的分类 按交换方式分类 按使用者分类 按传输介质分类 按覆盖范围分类 按拓扑结构分类,可…

数据结构之deque双端队列

一、概念: 众所周知,数据结构是用来存储数据,deque也不例外,他是集结了队列和栈的性质而成的结构,他几乎拥有所有数据结构能有的操作,看似已经大杀四方,可实际情况如何呢,那就带者这…

markdown页面宽度放宽

变成以上样式 ------------------------------------------------ 然后最后一行加上 #write{ max-width: 90%; } /* 调整源码正文宽度 */ #typora-source .CodeMirror-lines { max-width: 90%; } /* 调整输出 PDF 文件宽度 */ media print { #write{ max-w…

C++字符串操作【超详细】

零.前言 本文将重点围绕C的字符串来展开描述。 其中,对于C/C中字符串的一些区别也做出了回答,并对于C的(string库)进行了讲解,最后我们给出字符串的不同表达形式。 开发环境: VS2022 一.字符串常量跟字…

UE4.27_ParticleSystem(没写完的材料)

UE4.27_ParticleSystem(没写完的材料) 参考实例: UE4[蓝图]下雪效果及雪的材质的实现

docker学习入门

1、docker简介 docker官网: www.docker.com dockerhub官网: hub.docker.com docker文档官网:docs.docker.com Docker是基于Go语言实现的云开源项目。 Docker的主要目标是:Build, Ship and Run Any App, Anywhere(构建&…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Image)

Image为图片组件,常用于在应用中显示图片。Image支持加载PixelMap、ResourceStr和DrawableDescriptor类型的数据源,支持png、jpg、jpeg、bmp、svg、webp和gif类型的图片格式。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容&am…

数据库系统概论(超详解!!!) 第三节 关系数据库

1.基本概念 1. 域(Domain) 域是一组具有相同数据类型的值的集合。 2. 笛卡尔积(Cartesian Product) 给定一组域D1,D2,…,Dn,允许其中某些域是相同的。 D1,D2…

phpStudy,自定义php版本

新版phpStudy,支持自定义php版本,只支持php5.3.0以后的任意版本。 一定要下载win版php,不是下载源码,win版php下载地址:http://windows.php.net/ 也就是地址: https://windows.php.net/downloads/releases…

使用Julia语言和R语言实现K-均值

K-均值算法基础 K-均值聚类算法属于一种无监督学习的方法,通过迭代的方式将数据划分为K个不重叠的子集(簇),每个子集由其内部数据点的平均值来表示。计算方法大体如下: 1.初始化簇中心 选择K个数据点作为初始的簇中心…

A5自媒体wordpress主题模板

一个简洁的wordpress个人博客主题,适合做个人博客,SEO优化效果挺不错的。 https://www.wpniu.com/themes/204.html

数据分析-Pandas最简单的方法画矩阵散点图

数据分析-Pandas直接画矩阵散点图 数据分析和处理中,难免会遇到各种数据,那么数据呈现怎样的规律呢?不管金融数据,风控数据,营销数据等等,莫不如此。如何通过图示展示数据的规律? 数据表&…

添加路障-蓝桥杯-DFS

自己另辟蹊径想的新思路 果然好像还是不太行呀 import java.util.Scanner;public class Main {static int T;//样例组数static int n;//矩阵大小static int[] X {0,1,0,-1};static int[] Y {1,0,-1,0};static int[] X1 {1,0,-1,0};static int[] Y1 {0,-1,0,1};static int …

Harbor二次开发前端环境搭建

1 前端开发环境搭建 (1)拉取分支代码 (2)前端开发推荐使用VsCode编辑器打开项目 打开 harbor\src\portal 文件夹,该文件夹为Harbor对应的前端代码所在位置 (3)在portal文件夹下创建名为 pro…