【问题标题】:Identity quirk with string split()带有字符串 split() 的身份怪癖
【发布时间】:2015-11-06 19:45:03
【问题描述】:
>>> 'hi'.split()[0] is 'hi'
    True    
>>> 'hi there'.split()[0] is 'hi'
    False
>>> 'hi there again'.split()[0] is 'hi'
    False

我的假设:

第一行在 split 中只有一个元素,而另外两行有多个元素。我相信,虽然像 str 这样的 Python 原语在函数内按值存储在内存中,但函数之间会有单独的分配以简化内存管理。我认为split() 是这些功能之一,它通常分配新的字符串。但它也处理不需要任何拆分的输入的边缘情况(例如'hi'),其中原始字符串引用被简单地返回。我的解释正确吗?

【问题讨论】:

  • 不,原因是'is'表示相同的内存位置它与'=='不同将你的比较更改为==并且它们会正常工作,'is'应该仅用于比较静态内存中的元素,例如 None 和 True / False

标签: python string split primitive


【解决方案1】:

我相信,虽然像 str 这样的 Python 原语在函数内按值存储在内存中,但函数之间会有单独的分配以简化内存管理。

Python 的对象分配不能像那样工作。没有真正的“原语”概念,除了字节码编译器为合并常量所做的一些事情之外,两个对象是在同一个函数中创建还是在不同函数中创建都没有关系。

没有比指出源更好的答案了,所以here it is

Py_LOCAL_INLINE(PyObject *)
STRINGLIB(split_whitespace)(PyObject* str_obj,
                           const STRINGLIB_CHAR* str, Py_ssize_t str_len,
                           Py_ssize_t maxcount)
{
    ...
#ifndef STRINGLIB_MUTABLE
        if (j == 0 && i == str_len && STRINGLIB_CHECK_EXACT(str_obj)) {
            /* No whitespace in str_obj, so just use it as list[0] */
            Py_INCREF(str_obj);
            PyList_SET_ITEM(list, 0, (PyObject *)str_obj);
            count++;
            break;
        }

如果它没有找到任何要拆分的空格,它只会重用返回列表中的原始字符串对象。这只是这个函数的编写方式的一个怪癖,你不能依赖它在其他 Python 版本或非标准 Python 实现中以这种方式工作。

【讨论】:

  • 这是一个很好的答案 - 但所有这些的真正意义应该是指出显而易见的 - 不要使用 'is' 来比较字符串 - 使用 '=='
  • @gkusner:在我看来,提问者已经明白了。该问题专门询问了对象分配策略,并且没有提及x is y对于相等的字符串xy应该为真。
【解决方案2】:

Python 中的所有数据都是通过引用存储的。 (C 实现中的PyObject*)您发现.split() 只是在找不到分隔符时返回self 作为优化。找到分隔符后,它必须为每个部分创建单独的字符串对象,因此它们是单独的对象。

(与 Java 不同,Java 对于“原始”和“引用/类类型”具有明显不同的数据类型,并且对它们的行为也不同)

【讨论】:

  • 嗯,我现在正在学习Java,不知道这个;那很有意思。很好的解释。
【解决方案3】:

就像我在评论中说的:

'hi there again'.split()[0] == 'hi'

>>True

实际上,您的问题有点确定 - 这是一种身份。

【讨论】:

  • 您并没有真正回答关于split() 的问题,为什么一个元素将具有相同的内存位置而多个元素不会。是因为每个函数都有自己的原始分配吗?
  • 基本上是的,字符串区域被散列以减少重复,因此字符串 Hi 只存储一次
猜你喜欢
  • 2015-05-29
  • 2010-11-21
  • 2011-12-10
  • 2015-03-29
  • 1970-01-01
  • 2012-02-08
  • 1970-01-01
相关资源
最近更新 更多