【问题标题】:Modify subclassed string in place就地修改子类字符串
【发布时间】:2010-12-23 15:52:52
【问题描述】:

我有以下字符串子类:

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 docs),但我认为我的实现中缺少一些关键部分。

【问题讨论】:

  • 文档告诉你 wrap 字符串类而不是子类!也就是说,要执行您想要的操作,您应该创建一个继承自 object 的类,其属性包含字符串的当前值。
  • [用手拍额头] 噢!谢谢你。谢谢你。请将您的上述评论作为答案(或将其添加到您现有的评论中),以便我接受。我想我错过了一些明显的东西。

标签: python subclassing


【解决方案1】:

你不能按照你的要求去做,因为字符串是不可变的。文档告诉您包装 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)。这更整洁、更快,而且 IMO 更优雅。

>>> 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 描述。

【讨论】:

  • 正如您在对我的问题的评论中所建议的那样,我只是将我的类更改为从object 继承并使用__init__ 来设置字符串值。效果很好!
  • 刷新后刚看到你的新例子。很好,彻底的答案。
【解决方案2】:

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

很抱歉,你不能做你想做的事。

【讨论】:

    【解决方案3】:

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

    在 UserString 模块中有一个名为 MutableString 的类,它可以做你想做的事。

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

    【讨论】:

      【解决方案4】:

      没有可变字符串。有字节/字节数组和单字符字符串列表,您可以对其进行修改,然后将其转换为字符串。如果你想模拟一个“可变字符串”,你必须在私有字段中保留一个字符串,替换它,否则假装你是那个字符串(这可能是MutableString 所做的)。但是请注意:这将非常低效,并且可能不需要。此外,您不能总是使用可变字符串代替不可变字符串(例如,作为 dict 键)。为什么你认为你需要一个可变字符串?我们其他人(以及 Java 和 .NET 人员)在没有的情况下相处得很好。

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

      【讨论】:

      • Python 中的一切都是参考。分配给 self 只是重新绑定名称。
      • @katrielalex:是的,每个变量都是一个引用,但是 Python 按值传递这些引用(或 C/C++ 中的指针)。
      【解决方案5】:

      这是您想要做的事情的实现:

      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
      

      你可以用更多的方法扩展这个类。

      【讨论】:

      • 您的示例仅适用于“子类”object 而不是 str
      • 谢谢,已编辑。我很想继承str 的所有默认方法。但是,不知道如果我添加了自定义方法,它将无法正常工作。如果我们继承str,我们不能添加自己的方法吗?
      猜你喜欢
      • 1970-01-01
      • 2012-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-07
      • 1970-01-01
      • 1970-01-01
      • 2021-12-13
      相关资源
      最近更新 更多