就地修改子类字符串

问题描述 投票:0回答:6

我有以下字符串子类:

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文档),但我认为我在实现中遗漏了一些关键部分。

python subclassing
6个回答
5
投票

您无法执行您所要求的操作,因为字符串是不可变的。文档告诉你 wrap

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

如果您希望

__repr__
看起来更像字符串,则可以覆盖
x
,但这是不好的做法,因为在可能的情况下
__repr__
旨在返回对象的 Python 描述。


3
投票

self = self + delimiter + next_val
正在创建一个new变量
self
并将
self + delimiter + next_val
的结果分配给它。要实现您想要的效果,您需要将操作直接应用于
self
变量。但由于字符串是不可变的,所以你不能这样做。这正是所有
str
方法返回一个新字符串而不是修改它们所操作的字符串的原因。

很抱歉,您无法完成您想要完成的任务。


1
投票

Python 字符串(以及从它们继承的任何内容)是不可变的。

UserString 模块中有一个名为 MutableString 的类可以做你想要的事情。

如果您使用的是最新版本(如 2.7/3.1)的 python,您还可以查看 bytearray,尽管它有自己的一组限制和怪癖。


0
投票

没有可变字符串。有字节/字节数组和单字符字符串列表,您可以修改它们然后将其转换为字符串。如果你想模拟一个“可变字符串”,你必须将一个字符串保留在私有字段中,替换它,否则假装你就是那个字符串(这可能就是

MutableString
所做的)。但请注意:这将是非常低效的,而且可能是不需要的。此外,您不能总是使用可变字符串来代替不可变字符串(例如作为字典键)。为什么你认为你需要一个可变字符串?我们其他人(以及 Java 和 .NET 人员)在没有的情况下也相处得很好。

你的

conc
不起作用,因为Python没有引用传递。
self = ...
不会更改当前对象,它只是覆盖局部变量(
self.member = ...
确实工作,因为这是修改某些字典的方法调用)。


0
投票

这是您想要执行的操作的实现:

class S(object):
    def __init__(self, val=""):
        self.data = val;

    def conc(self, next_val, delimiter = ' '):
        if not next_val is None:
            self.data = self.data + delimiter + next_val
        return self

    def __repr__(self):
        return self.data

您可以使用更多方法扩展此类。


0
投票

我通过使用 str 和 UserString 进行子类化解决了这个问题。

从集合导入 UserString

class String(str, UserString):
    def __init__(self, string: str):
        super().__init__(string)

    def __repr__(self):
        return self.string

这有点hacky,但它确实解决了我在调用例如“isinstance(var, str)”时被读取为字符串的问题,同时能够修改类本身的内部结构。

我知道这不是正确的形式,但是,当 Python 不允许将由文本组成的类视为字符串时,选项会受到限制。

本质上,我需要字符串中包含的其他信息是可修改的,并用于更新原始信息,这是允许的。所需的过程太复杂,无法简单地使用 str 类。

© www.soinside.com 2019 - 2024. All rights reserved.