在python中,some_string[5] = 'a'将是一个错误,但是最接近的等效操作some_string = some_string[5:] + 'a' + some_string[6:]确实是O(n)。但这不仅仅适用于不可变对象。对于连接列表也是如此:[1,2,3] + [4,5,6]生成一个新的列表,并且是O(n)。在
加上数字会创建一个新值,但通常结果值在内存中的大小总是相同的,所以它是O(1)。当然,这只适用于小整数。一旦达到某个阈值(在我的机器上是20位数字),INT会突然占用可变的空间量。我不知道这是如何影响渐近性能的。在
然而,我发现即使在log10(n) == 1000附近,它似乎也没有明显的效果:>>> times = [timeit.timeit(stmt=stmt.format(10 ** i, 10 ** i), number=100) for i in range(1000)]
>>> sum(times) * 1.0 / len(times)
3.0851364135742186e-06
>>> times[-1]
3.0994415283203125e-06
对于字符串,渐近性能下降更为明显:
^{pr2}$
最后一个操作的执行时间远低于平均值。而且趋势相当稳定:>>> for t in times[0:100000:10000]:
... print t
...
5.00679016113e-06
1.31130218506e-05
2.90870666504e-05
3.88622283936e-05
5.10215759277e-05
6.19888305664e-05
7.41481781006e-05
8.48770141602e-05
9.60826873779e-05
0.000108957290649
不过,像这样的小规模操作还是相当便宜的。在
为了扩展您的其他问题,索引访问在列表和字符串上都是O(1)。在>>> stmt = 'x = s[{0}] + s[{1}] + s[{2}]'
>>> setup = 's = "a" * {0}'
>>> times = [timeit.timeit(stmt=stmt.format(i / 2, i / 3, i / 4), setup=setup.format(i + 1), number=10) for i in range(1000000)]
>>> sum(times) * 1.0 / len(times)
3.6441037654876707e-06
>>> times[-1]
3.0994415283203125e-06
与列表类似:>>> stmt = 'x = s[{0}] + s[{1}] + s[{2}]'
>>> setup = 's = ["a"] * {0}'
>>> times = [timeit.timeit(stmt=stmt.format(i / 2, i / 3, i / 4), setup=setup.format(i + 1), number=10) for i in range(100000)]
>>> sum(times) * 1.0 / len(times)
2.8617620468139648e-06
>>> times[-1]
1.9073486328125e-06
切片复制字符串和列表,因此与n == len(slice)一起为O(n)。没有“好”的方法来替换字符串中的一个字母,尽管我想强调的是,“坏”的方法在大多数情况下已经足够好了。如果您想要一个“好”的方法,可以使用不同的数据类型;操作一个列表,并在需要字符串时连接它;或者使用StringIO对象。This page有一些关于连接不同内置Python数据类型的有用信息。在
最后,由于您对内部结构非常感兴趣,我在^{}中挖掘了struct的PyStringObject声明(来自版本2.7;3+可能看起来不同)。它是关于你所期望的一个c字符串和一些额外的铃声和口哨:typedef struct {
PyObject_VAR_HEAD
(PyObject_VAR_HEAD是一个c预处理器宏,它根据解释的规则here展开为类似下面的内容。)Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;
Py_ssize_t ob_size;
继续。。。在long ob_shash;
int ob_sstate;
char ob_sval[1];
/* Invariants:
* ob_sval contains space for 'ob_size+1' elements.
* ob_sval[ob_size] == 0.
* ob_shash is the hash of the string or -1 if not computed yet.
* ob_sstate != 0 iff the string object is in stringobject.c's
* 'interned' dictionary; in this case the two references
* from 'interned' to this object are *not counted* in ob_refcnt.
*/
} PyStringObject;
列表有一个similar structurec数组,它有额外的铃铛和哨声,但不是以null结尾的,并且通常有额外的预分配存储空间。在
不用说。。。这其中大部分只适用于cPythonPyPy,IronPython,和Jython看起来完全不同!在