【问题标题】:performance of get a specific character of a string in Python 2.7在 Python 2.7 中获取字符串的特定字符的性能
【发布时间】:2016-10-17 09:21:43
【问题描述】:

假设我想在 Python 2.7 中获取字符串的特定字符,假设

a = 'abcdefg...' # a long string
print a[5]

想知道何时访问字符串的任何特定字符,例如访问第 5 个元素,想知道性能是什么,是常数时间 O(1),还是线性性能 O(n),要么根据 5(位置我们正在访问的字符),还是对整个字符串的线性性能 O(n)(本例中为 len(a))?

问候, 林

【问题讨论】:

  • 我喜欢它可能是字符串长度上的线性性能的想法。就像,访问一个巨大字符串的第一个字符需要很长时间,因为它必须预先加载它,或者它存储在一些乱码的字典/链表中:) 我认为 Python 做了一些更奇怪的事情......

标签: python performance python-2.7


【解决方案1】:
>>> long_string_1M ="".join(random.choice(string.printable) for _ in xrange(1000000))
>>> short_string = "hello"
>>> timeit.timeit(lambda:long_string_1M[50000])
0.1487280547441503
>>> timeit.timeit(lambda:short_string[4])
0.1368805315209798
>>> timeit.timeit(lambda:short_string[random.randint(0,4)])
1.7327393072888242
>>> timeit.timeit(lambda:long_string_1M[random.randint(50000,100000)])
1.779330312345877

在我看来像 O(1)

他们实现了它,因为字符串是连续的内存位置,所以索引到它只是一个偏移的问题......如果你知道 c/c++ 它类似于*(pointer+offset),就没有搜索(至少这是我的理解) (我已经很久没有做 C 了,所以可能有点不对)

【讨论】:

  • 感谢 Joran,投票,我有同样的感觉/结果,并好奇它是如何在内部实现 O(1) 性能的?任何详细或高级的想法都会受到赞赏。 :)
  • 我假设它是因为wiki.python.org/moin/TimeComplexity 的详细信息......虽然他们没有特别标注字符串,但它足够接近列表......
  • 很好的参考 Joran。投票,并在允许我的情况下在 2 分钟内将您的回复标记为答案。你回答得太快了。 :)
  • 我同意 - 该表中有关元组的任何内容都可能应用于字符串(没有仔细看,但可能)。 python 文档有很多地方可以类比,实现类似
【解决方案2】:

除了 Joran 的回答,我会指给你 this reference implementation,确认他的回答是 O(1) 查找

/* String slice a[i:j] consists of characters a[i] ... a[j-1] */        
static PyObject *    
string_slice(register PyStringObject *a, register Py_ssize_t i,    
             register Py_ssize_t j)    
     /* j -- may be negative! */    
{    
    if (i < 0)    
        i = 0;    
    if (j < 0)    
        j = 0; /* Avoid signed/unsigned bug in next line */    
    if (j > Py_SIZE(a))    
        j = Py_SIZE(a);    
    if (i == 0 && j == Py_SIZE(a) && PyString_CheckExact(a)) {    
        /* It's the same as a */    
        Py_INCREF(a);    
        return (PyObject *)a;    
    }    
    if (j < i)  
        j = i;    
    return PyString_FromStringAndSize(a->ob_sval + i, j-i);    
}

为什么这应该是你的直觉

Python strings are immutable。这种常见的优化允许在需要时假设连续数据之类的技巧。请注意,在后台,我们有时只需要计算 C 中内存位置的偏移量(显然是特定于实现的)

There are several places 字符串的不变性是可以依赖(或烦恼)的东西。用python作者的话说;

[字符串不可变]有几个优点。一个是 性能:知道字符串是不可变的意味着我们可以分配 创建时的空间

因此,据我所知,尽管我们可能无法保证跨实现的这种行为,但可以假设是非常安全的。

【讨论】:

  • 大声笑 ... 伟大的思想都一样...(见我的编辑)很好的链接到我的 ref +1
  • @Joran haha​​ ya +1 on yours,当我输入我的时,那个编辑就进来了。我希望你不要插入我的链接,否则我将不得不取消帖子
  • @IharBury 感谢您对一个老问题的反馈 :) 为什么 PyString_FromStringAndSize 不是 O(1)?查看相同的参考实现,我没有看到任何“循环”;有一个 malloc 调用,你认为是“不是 O(1)”吗?我通常根本不考虑那些与字符串大小成正比的值,这似乎与其他 SO 答案一致:stackoverflow.com/questions/282926/…cs.stackexchange.com/questions/83834/…
  • @IharBury 嗯,在整个字符串中发生在哪里?确实,它可能会复制字符串的切片,但是 OP 询问的是访问单个字符,所以这将是一个元素上的 memcpy(并且在我看来,引用有一个特定的情况,对于 对于大小为 1 的对象,不是 memcpy,而是对它们进行实习(第 104 行)。请注意,对于 OP,j-1==1,我们不会在整个初始对象上调用 PyString_FromStringAndSize
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-20
  • 1970-01-01
  • 2019-04-22
  • 1970-01-01
  • 2011-05-13
相关资源
最近更新 更多