在CPython中,字符串不可变性被破坏了。

15

我在尝试理解一个Python模块时遇到了一个比较有趣的现象,这更多是一种“有趣”的现象,而不是寻求帮助(尽管解决方案也很有用)。

>>> import fuzzy
>>> s = fuzzy.Soundex(4)
>>> a = "apple"
>>> b = a
>>> sdx_a = s(a)
>>> sdx_a
'A140'
>>> a
'APPLE'
>>> b
'APPLE'

嗯,所以fuzzy模块完全违反了Python字符串的不变性。它能够这样做是因为它是一个C扩展吗?这是否构成了CPython和模块的错误,甚至安全风险?

此外,有没有人想到一种方法来解决这个问题?我希望能保留原始字符串的大小写。

谢谢,

Alex


我在生成的 C 代码中没有看到任何地方对字符串进行改变。 - Ignacio Vazquez-Abrams
@IgnacioVazquez-Abrams:也许我漏掉了什么,但它不是在__call__ [__pyx_f_5fuzzy_7Soundex___call__]中改变了它吗?它声明了一个cdef char ptr,将其设置为PyString_AsString调用的结果,然后修改其内容。 - DSM
@DSM:Bitbucket上的代码中没有它。我只看到从它上面读取,在891行 - Ignacio Vazquez-Abrams
@IgnacioVazquez-Abrams:啊,我在看发布版本,而不是主干。 - DSM
4个回答

13

这个故障在二月份被解决了; 请更新你的版本。

回答你的问题,是的,在C级别上有几种修改不可变类型的方法。此时安全影响未知,甚至可能无法得知。


谢谢您的回答!实际上,我三周前使用了easy_install来安装fuzzy。它给我的版本是fuzzy-1.0-py2.7-win-amd64.egg,正是这个版本出现了错误。 - Alex
@Alex:他们并不总是将其更新到最新版本;从Bitbucket安装。 - Ignacio Vazquez-Abrams

2

目前我没有可用的模糊模块进行测试,但以下代码可以创建一个具有新标识的字符串:

>>> a = "hello"
>>> b = ''.join(a)
>>> b
'hello'
>>> id(a), id(b)
(182894286096, 182894559280)

2
我对CPython不是很了解,但看起来在"fuzzy.c"中声明了"char *cs = s",其中"s"是传递给"__call__"的输入。然后它会改变"cs[i]",这显然会改变"s[i]",进而改变原始字符串。这绝对是Fuzzy存在的一个bug,你应该在bitbucket上报告它。正如Greg的回答所说,使用" ''.join(a)"将创建一个新的副本。

它已经在Bitbucket上提交了两次。 - Ignacio Vazquez-Abrams

0
如果更改了不可变字符串,那就是一个 bug,你可以通过以下方式解决:
s(a.upper())

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