在原地修改子类化字符串

6 投票
5 回答
1746 浏览
提问于 2025-04-16 08:57

我有一个字符串的子类:

class S(str):
    def conc(self, next_val, delimiter = ' '):
        """Concatenate values to an existing string"""
        if not next_val is None:
            self = self + delimiter + next_val
        return self

我希望它能这样工作:

>>> x = S("My")
>>> x.conc("name")
'My name'
>>> x
'My name'

结果却是这样:

>>> x = S("My")
>>> x.conc("name")
'My name'
>>> x
'My'

有没有办法直接修改这个字符串? 我觉得这涉及到可变字符串和不可变字符串的区别。创建子类似乎是把字符串当作可变对象处理的正确方法(至少根据Python文档),但我觉得我的实现中缺少了一些关键的部分。

5 个回答

1

Python中的字符串(还有从字符串派生的任何东西)是不可变的,也就是说一旦创建了字符串,你就不能改变它的内容。

在UserString模块里有一个叫做MutableString的类,可能可以满足你的需求。

如果你使用的是比较新的Python版本(比如2.7或3.1),你也可以看看bytearray,虽然它有自己的一些限制和特点。

3

这一行代码 self = self + delimiter + next_val 是在创建一个新的变量 self,并把 self + delimiter + next_val 的结果赋值给这个新的 self。如果你想实现你想要的效果,你需要直接对原来的 self 变量进行操作。但因为字符串是不可变的(也就是说,一旦创建就不能改变),你无法这样做。这就是为什么所有的 str 方法都会返回一个新的字符串,而不是直接修改它们操作的字符串。

所以很抱歉,你无法实现你想要的效果。

5

你问的事情是做不到的,因为字符串是不可变的。文档里告诉你要包装一下str类,也就是说你需要创建一个类,这个类里有一个属性,用来保存当前的“可变字符串”。在Python 2.x的标准库里有这个功能,叫UserString.MutableString(不过在Python 3里就没有了);不过其实自己写一个也挺简单的:

class MutableString(object):
    def __init__(self, value):
        self.value = value

    def conc(self, value, delim=' '):
        self.value = "{self.value}{delim}{value}".format(**locals())

    def __str__(self):
        return self.value

不过,更好的办法是使用StringIO。实际上,你可以通过继承StringIO来实现你想要的功能(注意要使用纯Python版本,而不是C版本来做,而且它是旧式类,所以不能用super)。这样做更整洁、更快,而且我觉得更优雅。

>>> from StringIO import StringIO as sIO
>>> class DelimitedStringIO(sIO):
...     def __init__(self, initial, *args, **kwargs):
...             sIO.__init__(self, *args, **kwargs)
...             self.write(initial)
...
...     def conc(self, value, delim=" "):
...             self.write(delim)
...             self.write(value)
...
...     def __str__(self):
...             return self.getvalue()
...
>>> x = DelimitedStringIO("Hello")
>>> x.conc("Alice")
>>> x.conc("Bob", delim=", ")
>>> x.conc("Charlie", delim=", and ")
>>> print x
Hello Alice, Bob, and Charlie

如果你想让x看起来更像一个字符串,可以重写__repr__,但这其实是不太好的做法,因为__repr__的目的是返回对象在Python中的描述,尽量不要随便改。

撰写回答