- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
假设我们有一个字符串列表,我们想通过连接这个列表中的所有元素来创建一个字符串。像这样:
def foo(str_lst):
result = ''
for element in str_lst:
result += element
return result
由于字符串是不可变对象(immutable对象),我希望 python 在每次迭代时创建一个新的 str 对象并复制 result 和 element 的内容。它使 O(M * N^2)
时间复杂度,M 是每个元素的长度,N 是列表的大小。
但是,我的实验表明它以线性时间运行。
N = 1000000 # 1 million
str_lst = ['a' for _ in range(N)]
foo(str_lst) # It takes around 0.5 seconds
N = 2000000 # 2 million
str_lst = ['a' for _ in range(N)]
foo(str_lst) # It takes around 1.0 seconds
N = 10000000 # 10 million
str_lst = ['a' for _ in range(N)]
foo(str_lst) # It takes around 5.3 seconds
我怀疑 python 在后台使用了类似 stringbuffer 的东西。因此,它不会在每次迭代时创建新对象。
现在考虑一个稍微不同的实现。唯一的区别是一项额外的作业。
def foo2(str_lst):
result = ''
for element in str_lst:
result += element
temp = result # new added line
return result
我知道 temp = result
行不会创建新对象。 temp
只是指向同一个对象。所以,这个小改动应该不会对性能产生太大影响。
N = 1000000 # 1 million
str_lst = ['a' for _ in range(N)]
foo(str_lst) # It takes around 0.5 seconds
foo2(str_lst) # It takes around 30 seconds
N = 2000000 # 2 million
str_lst = ['a' for _ in range(N)]
foo(str_lst) # It takes around 1 seconds
foo2(str_lst) # It takes around 129 seconds
但是,有很大的不同。看起来 foo2 函数是 O(N^2) 而 foo 是 O(N)。
我的问题是 python 如何在不破坏不可变对象(immutable对象)分配等其他语言组件的情况下实现字符串连接的线性时间?那条额外的线是如何影响性能的呢?我在 cpython 实现中搜索了一下,但找不到确切位置。
更新
这是线路分析结果。
foo 函数的结果
Total time: 0.545577 s
File: <ipython-input-38-b9bb169e8fe0>
Function: foo at line 1
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1 def foo(str_lst):
2 1 2.0 2.0 0.0 result = ''
3 1000001 238820.0 0.2 43.8 for element in str_lst:
4 1000000 306755.0 0.3 56.2 result += element
5 1 0.0 0.0 0.0 return result
foo2 函数的结果
Total time: 30.6663 s
File: <ipython-input-40-34dd53670dd9>
Function: foo2 at line 1
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1 def foo2(str_lst):
2 1 2.0 2.0 0.0 result = ''
3 1000001 299122.0 0.3 1.0 for element in str_lst:
4 1000000 30033127.0 30.0 97.7 result += element
5 1000000 413283.0 0.4 1.3 temp = result
6 1 0.0 0.0 0.0 return result
temp = result
行以某种方式影响了 result += element
行的性能。
最佳答案
让另一个名称指向同一个对象会破坏优化。优化基本上通过 resizing the string object 起作用并附加到位。如果对该对象有多个引用,则无法在不影响其他引用的情况下调整大小。由于字符串是不可变的,允许这将是实现的一个严重缺陷。
temp = result
增加了由 result
命名的字符串对象的引用计数,从而禁止优化。
在 +=
情况下执行的完整检查列表(最终转换为 PyUnicode_Append
)可以在 unicode_modifiable
中看到功能。除其他事项外,它检查对象的引用计数是否等于 1、它不是 interned 以及它不是字符串子类。
the if
statement 中还有几项检查如果您想要更详尽的列表,请注意此优化。
虽然这不是您问题的基本问题,但 future 的读者可能会对如何有效地执行字符串连接感到好奇。除了关于 S.O 的类似问题,Python FAQ also has an entry对此。
关于Python 字符串连接内部细节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55063068/
前言 本文主要介绍了关于MySQL主键为0与主键自排约束的关系,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。 开始不设置主键表的设计如下: 如果id的位置有好几个0
我已经阅读了一些关于将消息从一个线程冒泡到所有其他线程以正常退出的正确方法的来源(每个线程都执行它自己的退出例程)。其中,我喜欢全局原子 bool 值的想法,它可以从任何线程进行标记,所有其他线程检查
本文深入探讨Go语言中的流程控制语法,包括基本的 if-else 条件分支、 for 循环、 switch-case 多条件分支,以及与特定数据类型相关的流程控制,如 for-r
我是 MVC 和 XCode 的新手,在将我对 MVC 的概念理解转化为设计和实现具体类时遇到了困难。我希望就如何构建 Controller 和 View 以获得预期的 UI 获得一些建议。这是针对
如果我尝试在 View 中打开 DeatilFragement,我的应用程序崩溃并收到以下错误: Caused by: java.lang.IllegalStateException: Require
我正在尝试构建我的 iOS 应用程序的界面。一遍又一遍地开始新项目我仍然遇到细节 View 控件的问题(见图)。 在这里我得到了截图: 详细 View 显示当用户触摸 UITableView 行时。您
我在与我正在处理的项目的类(class)中遇到问题。该类是一个接受标签和值的 GUI 组件。这里的想法是,用户可以指定一个标签,然后从任何地方链接一个值(更具体地说,该值的 ToString 方法),
嗯.. 我在我的应用程序中设置了表格 View - 详细 View 。 主视图使用常规代码将数据传递给详细 View - (void)tableView:(UITableView *)tableVie
我有 celery 任务,队列中有 100 个输入数据,需要使用 5 个 worker 来执行。 如何获取哪个工作人员正在执行哪个输入? 每个 worker 执行了多少输入及其状态? 如果任何任务失败
我有一个 .net github 项目,它基本上是一个 Web API 的包装器。在测试项目中,我使用 API key 调用 API。我需要将此 key 保密,如何在 Visual Studio 项目
我遇到一个问题,从 Ag-Grid 导出网格只会导出主网格的详细信息,而不会导出子网格。这是一个显示问题的 plunkr: https://next.plnkr.co/edit/jVcvWDJ1NKP
我在详细 View 中有一个不会消失的额外空间。该 View 来自 NavigationLink,但我已经尝试过使用或不使用 NavigationView。我试图包装它用 NavigationView
几天来,我一直在关注猫效应和 IO。我觉得我对这种效果有一些误解,或者只是我错过了它的重点。 首先——如果IO可以替代Scala的Future,我们如何创建异步IO任务?使用 IO.shift ?使用
如何将标高添加到主视图/详细 View 的详细信息 Pane 中,以在其下方提供阴影,同时定位为部分覆盖工具栏(如下面的底部图片)?我尝试使用 android:elevation="4dp" 但这对我
我试图在我的 UISplitViewController 的细节 View 上设置一个阴影,我希望在 iOS 6 中的主 View 上可见。 在我的细节 View Controller 中: sel
我正在阅读 std::basic_string::reserve(size_type res_arg=0) 上的标准.它是这样说的: void reserve(size_type res_arg=0)
Boost 文档说 Starting with Boost release 1.53, shared_ptr can be used to hold a pointer to a dynamicall
我用 OpenGL 编写了一个简单的 24 位位图加载器。我打开一个位图文件并读取它的像素,然后从中创建一个 RGB 像素数据数组,然后将其传递给 glDrawPixels()。 问题:我需要使图像的
for x in ...循环 就是把每个元素代入变量x,然后执行缩进块的语句。 range()函数,可以生成一个整数序列,再通过list()函数可以转换为list。 比如我们想计算1-10的整数
场景 我有一个 DevExpress XtraGrid。 显示的数据采用主/详细信息格式,点击行开头的“+”可展开该主行的详细信息。 我通过将网格数据源绑定(bind)到包含自己的字典属性(以保存详细
我是一名优秀的程序员,十分优秀!