简短版本:如果s是一个字符串,那么s=s“c”可能会在适当的位置修改字符串,而t=s“c”不能。但是操作如何知道它在哪种情况下?
长版本:
t=s“c”
需要创建一个单独的字符串,因为程序随后希望旧字符串为s,新字符串为t。
如果s
是唯一的参考,则s=s“c”
可以修改字符串,因为程序只希望s
是扩展字符串。如果末尾有多余的字符空间,CPython实际上会进行这种优化。
考虑这些函数,它们重复添加一个字符:
def fast(n):
s = ''
for _ in range(n):
s = s + 'c'
t = s
del t
def slow(n):
s = ''
for _ in range(n):
t = s + 'c'
s = t
del t
使用n=100_000
对结果进行基准测试(在线尝试!):
fast : 9 ms 9 ms 9 ms 9 ms 10 ms
slow : 924 ms 927 ms 931 ms 933 ms 945 ms
请注意,额外的t=s或s=t使这两个变量都等效于字符串的引用,然后del t只剩下s,因此在下一次循环迭代中,s再次是字符串的唯一引用。因此,这两个函数之间的唯一区别是分配给s和t的顺序。
我们还要反汇编字节码。我用
标记了仅有的三个差异=
在中间。正如预期的那样,只有STORE\u FAST和LOAD\u FAST的变量不同。但是,直到并包括BINARY\u ADD,字节码是相同的。那么,BINARY\u ADD如何知道是否要优化呢?
import dis import dis
dis.dis(fast) dis.dis(slow)
---------------------------------------------------------------------------
0 LOAD_CONST 1 ('') 0 LOAD_CONST 1 ('')
2 STORE_FAST 1 (s) 2 STORE_FAST 1 (s)
4 LOAD_GLOBAL 0 (range) 4 LOAD_GLOBAL 0 (range)
6 LOAD_FAST 0 (n) 6 LOAD_FAST 0 (n)
8 CALL_FUNCTION 1 8 CALL_FUNCTION 1
10 GET_ITER 10 GET_ITER
>> 12 FOR_ITER 18 (to 32) >> 12 FOR_ITER 18 (to 32)
14 STORE_FAST 2 (_) 14 STORE_FAST 2 (_)
16 LOAD_FAST 1 (s) 16 LOAD_FAST 1 (s)
18 LOAD_CONST 2 ('c') 18 LOAD_CONST 2 ('c')
20 BINARY_ADD 20 BINARY_ADD
22 STORE_FAST 1 (s) != 22 STORE_FAST 3 (t)
24 LOAD_FAST 1 (s) != 24 LOAD_FAST 3 (t)
26 STORE_FAST 3 (t) != 26 STORE_FAST 1 (s)
28 DELETE_FAST 3 (t) 28 DELETE_FAST 3 (t)
30 JUMP_ABSOLUTE 12 30 JUMP_ABSOLUTE 12
>> 32 LOAD_CONST 0 (None) >> 32 LOAD_CONST 0 (None)
34 RETURN_VALUE 34 RETURN_VALUE
下面是有问题的代码,来自Python 3.10分支(在ceval.c中,并从同一文件的二进制添加操作码实现中调用)。正如@jasonharper在一篇评论中所指出的,它会向前看,看看二进制加法的结果是否会被绑定到左手加数所在的名称上。在快速(code)中,它是(操作数来自s,结果存储到s中),但在慢速(code)中它不是(操作数来自s,但存储到t中)。
不过,不能保证这种优化会持续下去。例如,我注意到您的fast()
并不比当前开发的CPythonmain
分支上的slow()
快(这是目前正在进行的最终3.11版本的工作)。
如前所述,不能保证这种优化会持续下去。“严肃的”Python程序员应该知道不要依赖不可靠的CPython特定技巧,事实上,PEP 8明确警告不要依赖这个特定的技巧:
代码的编写方式应不影响其他Python实现(PyPy、Jython、IronPython、Cython、Psyco等)。
例如,不要依赖CPython对a=b
或a=a b
形式的语句的就地字符串连接的有效实现...
问题内容: 希望这只是一个简单的问题,涉及到Sql 2008中的查询时的性能优化。 我曾为在其ETL流程以及一些网站中经常使用Stored Procs的公司工作。我已经看到了他们需要基于一组有限的键值来检索特定记录的情况。我已经看到它以3种不同的方式进行处理,下面的伪代码对此进行了说明。 动态SQL,它包含一个字符串并执行它。 使用用户定义的函数将定界字符串拆分为表 使用XML作为参数而不是带分隔
我需要解析“txf”格式的数据文件。这些文件可能包含 1000 多个条目。由于格式像JSON一样定义得很好,我想做一个像JSON这样的通用解析器,它可以序列化和解串化txf文件。 与JSON相反,标记没有办法识别对象或数组。如果一个带有相同标签的条目出现,我们需要把它看作一个数组。 标记对象的开始。 标记对象的成员 标记对象的结尾 下面是一个示例“txf”文件 我能够使用NSScanner创建通用
问题内容: 这是我的代码: 导致此错误: 可捕获的致命错误:传递给phpwtf()的参数1必须是字符串的一个实例,字符串给定 看到PHP能够一口气识别并拒绝所需的类型,这不只是一点点Orwellian。 该死的,有五个灯。 PHP中字符串的类型提示等效于什么?对答案的额外考虑,可以准确解释这里发生的情况。 问题答案: 在PHP7之前,类型提示只能用于强制对象和数组的类型。标量类型不是类型隐含的。在
问题内容: 字符串池是什么意思?以下声明之间有什么区别: JVM存储这两个字符串之间有什么区别吗? 问题答案: 字符串池是JVM对字符串实习概念的特定实现: 在计算机科学中,字符串保留是一种仅存储每个不同的字符串值的一个副本的方法,该值必须是不变的。内部字符串使某些字符串处理任务更加节省时间或空间,但代价是在创建或嵌入字符串时需要更多时间。不同的值存储在字符串内部存储池中。 基本上,字符串内部存储
问题内容: 因此,最近我一直在制作python脚本,用于从大型文本文件(> 1 GB)中提取数据。问题基本上可以归结为从文件中选择文本行,然后从某个数组中搜索字符串以查找字符串(此数组中可以包含多达1000个字符串)。这里的问题是我必须找到该字符串的特定出现,并且该字符串在该文件中可能出现无数次。同样,需要一些解码和编码,这另外会降低脚本速度。代码看起来像这样: 我的问题是:有没有办法对此进行优化
18.6. 优化字符串操作 Soundex 算法的最后一步是对短结果补零和截短长结果。最佳的做法是什么? 这是目前在 soundex/stage2/soundex2c.py 中的做法: digits3 = re.sub('9', '', digits2) while len(digits3) < 4: digits3 += "0" return digit