【问题标题】:Inheriting from "str" class从“str”类继承
【发布时间】:2014-02-17 11:50:12
【问题描述】:

我正在做这个小练习... 我想根据一些奇怪的字典重新排序一个字符串。 例如,根据我的字典,字母按顺序排列: “a”、“b”、“d”、“c”、“f”、“e”

所以我想我应该为字符串重载

这里是:

class MyString(str):
     new_dict = dict((x,i) for i,x in enumerate(["a", "b", "d", "c", "f", "e"]))
     def __lt__(self,other):
         return self.new_dict[self] < self.new_dict[other]
     def __init__(self,x):
         str.__init__(self,x)

然后

In [59]: sorted((MyString(x) for x in "abcdef"))
Out[59]: ['a', 'b', 'd', 'c', 'f', 'e']

太棒了。甚至:

In [64]: MyString("".join(sorted((MyString(x) for x in "abcdef"))))
Out[64]: 'abdcfe'

但为什么我不能只做sorted(MyString("abcdef"))

In [70]: sorted(MyString("abcdef"))
Out[70]: ['a', 'b', 'c', 'd', 'e', 'f']

显然 MyString 的迭代器正在返回字符串。

In [72]: for i in MyString("abcdef"):
             print type(i)
      ....:     
      <type 'str'>
      <type 'str'>
      <type 'str'>
      <type 'str'>
      <type 'str'>
      <type 'str'>

如果我在 MyString 上调用 join 会发生什么:

In [63]: type(MyString("").join(sorted((MyString(x) for x in "abcdef"))))
Out[63]: str

为什么 MyString 有 str 迭代器?

【问题讨论】:

  • sorted() 采用一个关键函数,让您可以为已排序的元素定义排序值。
  • 否则,排序使用&gt;&gt;=&lt;===同样需要提供all运算符。

标签: python inheritance operator-overloading


【解决方案1】:

你需要在这里覆盖__getitem__ method

class MyString(str):
    def __getitem__(self, i):
        return type(self)(super(MyString, self).__getitem__(i))

这会返回当前类型的新实例:

>>> for i in MyString("abcdef"):
...     print type(i)
... 
<class '__main__.MyString'>
<class '__main__.MyString'>
<class '__main__.MyString'>
<class '__main__.MyString'>
<class '__main__.MyString'>
<class '__main__.MyString'>

str 本身没有实现迭代(它没有__iter__ menthod,但确实 实现了序列协议(它有__len__ 长度方法和__getitem__ 方法) ; 这是for 循环最终使用的)。

如果使用 Python 3,str 对象确实有一个 __iter__ 方法,您需要改写它:

class MyString(str):
    def __iter__(self):
        return (type(self)(i) for i in super().__iter__())

注意str是不可变类型,覆盖__init__对实例影响不大。

对于订购,你真的需要实现所有__gt____ge____eq__等方法。使用@functools.total_ordering() decorator 可以节省大部分工作:

from functools import total_ordering

@total_ordering
class MyString(str):
    sortmap = {x: i for i, x in enumerate("abdcfe")}

    def __lt__(self, other):
        return self.sortmap[self] < self.sortmap[other]

    # inherit __eq__ from str

    def __getitem__(self, i):
        return type(self)(super(MyString, self).__getitem__(i))

最后但同样重要的是,对于排序,只需在此处使用 key 参数到 sorted()

>>> sortmap = {x: i for i, x in enumerate("abdcfe")}
>>> sorted('abcdef', key=sortmap.get)
['a', 'b', 'd', 'c', 'f', 'e']

【讨论】:

  • 那是无限递归,for i in self会调用你自己的__iter__方法。
  • @filmor:哎呀,我首先专注于其他事情。已更正。
  • 我只是在玩这个试图学习新事物。你的回答是惊人的。谢谢!
  • 那么从不可变类继承的任何东西都是不可变的?从 mutable 继承的任何东西都是可变的?有没有办法改变它?只是好奇。
  • @Diana:不,不是这样,真的。您不能更改不可变组件(str 值),但可以更改您为子类提供的其他属性。但是,您不会在课堂上这样做,因此 __init__ 没有实际价值。
【解决方案2】:

您不需要子类来自定义排序行为 - 您可以将key 参数传递给sort 方法或sorted 调用,指定一个函数 给出被比较的每个元素的相对权重。

喜欢:

def mycomp(文本): myseq = ("abdcfe") 加权 = [myseq.find(char) for char in text] return weighted # 这将为映射字符串中未找到的字符放置 -1

【讨论】:

    【解决方案3】:

    您确实应该使用key 参数而不是您的方法。然而,它不起作用的原因仅仅是你没有重载 __iter__ 函数:

    class MyString(str):
        # ...
        def __iter__(self):
            for x in super().__iter__():
                yield self.__class__(x)
    

    在 Python 2 中你可以使用

    class MyString(str):
        # ...
        def __iter__(self):
            for x in super(MyString, self).__str__():
                yield self.__class__(x)
    

    【讨论】:

    • 这只适用于 Python 3; Python 2 str.__iter__ 不存在。
    猜你喜欢
    • 2011-02-10
    • 1970-01-01
    • 1970-01-01
    • 2021-11-25
    • 1970-01-01
    • 2023-03-27
    • 2018-05-23
    • 1970-01-01
    相关资源
    最近更新 更多