字符串分割(split())的身份特性问题

5
>>> 'hi'.split()[0] is 'hi'
    True    
>>> 'hi there'.split()[0] is 'hi'
    False
>>> 'hi there again'.split()[0] is 'hi'
    False

我的假设:

第一行只有一个元素,而其他两行有多个元素。我认为 Python 原始数据类型(例如 str)在函数内部以值的形式存储在内存中,跨函数之间将分配不同的内存地址以简化内存管理。我认为 split() 就是其中之一,它通常会分配新的字符串。但同时也处理了输入不需要分割的边界情况(如 'hi'),这时会直接返回原始字符串的引用。我的解释是否正确?


不行,原因是“is”比较的是内存地址,而不是“==”比较的值。将比较操作符改为“==”,程序就能正常运行了。“is”只应该用于比较静态内存中的元素,如None、True/False。 - gkusner
3个回答

1
我认为,虽然像str这样的Python基本类型在函数内以值的形式存储在内存中,但为了简化内存管理,在函数之间会有不同的分配。
Python的对象分配并不像这样工作。没有真正的“基本类型”的概念,除了字节码编译器做的一些事情来合并常量之外,它并不关心两个对象是在同一个函数中创建还是在不同的函数中创建。
对此没有更好的答案,只能指向源代码,所以在这里
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实现以相同的方式工作。

1
这是一个很好的答案 - 但所有这些的真正重点应该是指出显而易见的事实 - 不要使用“is”来比较字符串 - 使用“==”。 - gkusner
2
@gkusner:在我看来,提问者已经理解了这一点。问题明确询问了对象分配策略,并没有提到对于相等的字符串xyx is y应该为真的任何期望。 - user2357112

0

所以就像我在评论中说的:

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

>>True

实际上,你的问题已经说得很清楚了——它是一个身份标识。


1
你并没有真正回答关于 split() 的问题,为什么一个元素会有相同的内存位置,但是多个元素却不会。这是因为每个函数都有自己的原始分配吗? - onepiece
基本上是的,字符串区域被哈希化以减少重复,因此字符串“Hi”只存储一次。 - gkusner

0

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

(与Java不同,Java对于“原始类型”和“引用/类类型”有明显不同的数据类型,并且在处理它们时表现不同)


嗯,我目前正在学习Java,之前不知道这个;很有趣。解释得很好。 - Saroekin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接