如何在Python中继承str

yas*_*sar 26 python fluent-interface subclassing

我试图继承str对象,并添加几个方法.我的主要目的是学习如何做到这一点.我被困在哪里,我是否应该在元类中继承字符串,并使用该元或子类str直接创建我的类?

而且,我想我需要以__new__()某种方式实现,因为,我的自定义方法将修改我的字符串对象,并将返回新的mystr obj.

我的类的方法应该可以使用str方法完全链接,并且应该在自定义方法修改它时始终返回一个新的我的类实例.我希望能够做到这样的事情:

a = mystr("something")
b = a.lower().mycustommethod().myothercustommethod().capitalize()
issubclass(b,mystr) # True
Run Code Online (Sandbox Code Playgroud)

我希望拥有它拥有的所有能力str.例如,a = mystr("something")然后我想使用它,如a.capitalize().mycustommethod().lower()

我的理解是,我需要实施__new__().我想是这样的,因为,字符串方法可能会尝试创建新的str实例.所以,如果我覆盖__new__(),他们应该会返回我的自定义str类.但是,__init__()在这种情况下,我不知道如何将参数传递给我的自定义类的方法.我想我需要使用type()才能在__new__()方法中创建一个新实例吗?

sth*_*sth 33

__new__()如果要在构造中修改字符串,则覆盖有效:

class caps(str):
   def __new__(cls, content):
      return str.__new__(cls, content.upper())
Run Code Online (Sandbox Code Playgroud)

但是如果你只是想添加新方法,你甚至不必触及构造函数:

class text(str):
   def duplicate(self):
      return text(self + self)
Run Code Online (Sandbox Code Playgroud)

请注意,继承的方法,例如upper()仍将返回正常str,而不是text.

  • 基于 __init__(self, *args, **kwargs) + super() 的更通用方法正在报告 DeprecationWarning: object.__init__() 不带参数。因此,我认为 __new__(cls, *args, **kwargs) 是更好的方法。或者?也许这解释得更好:http://jfine-python-classes.readthedocs.org/en/latest/subclass-int.html (2认同)

Aar*_*all 14

我试图继承str对象,并添加几个方法.我的主要目的是学习如何做到这一点.

不要将方法硬编码到父类(如最常见的答案).相反,使用super这样来支持Python 2:

class Caps(str):
    def __new__(cls, content):
        return super(Caps, cls).__new__(cls, content.upper())
Run Code Online (Sandbox Code Playgroud)

在Python 3中,这样调用更高效super,但它不向后兼容Python 2:

class Caps(str):
    def __new__(cls, content):
        return super().__new__(cls, content.upper())
Run Code Online (Sandbox Code Playgroud)

用法:

>>> Caps('foo')
'FOO'
>>> isinstance(Caps('foo'), Caps)
True
>>> isinstance(Caps('foo'), str)
True
Run Code Online (Sandbox Code Playgroud)

完整的答案

到目前为止,没有一个答案符合您的要求:

我的类的方法应该可以使用str方法完全链接,并且应该在自定义方法修改它时始终返回一个新的我的类实例.我希望能够做到这样的事情:

a = mystr("something")
b = a.lower().mycustommethod().myothercustommethod().capitalize()
issubclass(b,mystr) # True
Run Code Online (Sandbox Code Playgroud)

(我相信你的意思isinstance(),不是issubclass().)

您需要一种方法来拦截字符串方法.__getattribute__做这个.

class Caps(str):
    def __new__(cls, content):
        return super(Caps, cls).__new__(cls, content.upper())
    def __repr__(self):
        """A repr is useful for debugging"""
        return f'{type(self).__name__}({super().__repr__()})'
    def __getattribute__(self, name):
        if name in dir(str): # only handle str methods here
            def method(self, *args, **kwargs):
                value = getattr(super(), name)(*args, **kwargs)
                # not every string method returns a str:
                if isinstance(value, str):
                    return type(self)(value)  
                elif isinstance(value, list):
                    return [type(self)(i) for i in value]
                elif isinstance(value, tuple):
                    return tuple(type(self)(i) for i in value)
                else: # dict, bool, or int
                    return value
            return method.__get__(self) # bound method 
        else: # delegate to parent
            return super().__getattribute__(name)
    def mycustommethod(self): # shout
        return type(self)(self + '!')
    def myothercustommethod(self): # shout harder
        return type(self)(self + '!!')
Run Code Online (Sandbox Code Playgroud)

现在:

>>> a = Caps("something")
>>> a.lower()
Caps('SOMETHING')
>>> a.casefold()
Caps('SOMETHING')
>>> a.swapcase()
Caps('SOMETHING')
>>> a.index('T')
4
>>> a.strip().split('E')
[Caps('SOM'), Caps('THING')]
Run Code Online (Sandbox Code Playgroud)

并要求案件工作:

>>> a.lower().mycustommethod().myothercustommethod().capitalize()
Caps('SOMETHING!!!')
Run Code Online (Sandbox Code Playgroud)


And*_*zko 12

我对其他答案的复杂性感到震惊,python标准库也是如此.您可以使用collections.UserString来对字符串进行子类化,并且不要使用代理str的方法.

只需将其子类化,然后添加您的方法.self.data包含由对象表示的实际字符串,因此您甚至可以通过在self.data内部重新分配来实现str-"mutating"方法.

一个例子.

  • 但 UserString 不是 str 的子类,因此如果您需要 isinstance 检查才能工作,则这不是一个选项。 (4认同)
  • **另请参阅:**`six.moves UserString`https://stackoverflow.com/search?q="six.moves"https://pythonhosted.org/six/#module-six.moves (2认同)
  • 很好的答案+1。不是 json 可序列化的。 (2认同)

Unc*_*eiv 8

这是一个快速破解你想做的事情:你基本上拦截每个函数调用,如果你看到它返回一个字符串,你将它转换回你自己的类类型.

虽然这适用于这个简单的例子,但它有一些局限性.除其他外,下标运营商等运营商显然没有得到处理.

class FunWrapper(object):
    def __init__(self, attr):
        self.attr = attr

    def __call__(self, *params, **args):
        ret = self.attr(*params, **args)
        if type(ret) is str:
            return Foo(ret)
        return ret

class Foo(object):
    def __init__(self, string):
        self.string = string

    def __getattr__(self, attr):
        return FunWrapper(getattr(self.string, attr))

    def newMethod(self):
        return "*%s*" % self.string.upper()


f = Foo('hello')
print f.upper().newMethod().lower()
Run Code Online (Sandbox Code Playgroud)